#include <igraph.h>
#include <string.h>
#include <stdlib.h>
#include <unordered_map>
#include <fstream>
#include <sstream>
#include <string>
#include <iostream>
#include <chrono>
#include <iosfwd>
#include <map>
#include <algorithm>
#include <cctype>
#include <random>
#include <functional>
#include <windows.h>
#include <stdio.h>
#include <psapi.h>
#include <iostream>
#include <string>
#include <unordered_map>
#include <algorithm>

#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>

#include "DataHolder.h"
#include <regex>
#include <iomanip>

using std::chrono::duration_cast;
using std::chrono::microseconds;
using std::chrono::steady_clock;

DataHolder g_Data;

igraph_t g_orig;

int g_idVecToRemove = -1;
int g_addonsOnly = 0;
int g_uid = 0;
int g_maxiter = 10;
bool g_bRemoveTerms = false;
bool g_bRemoveSpecialTerms = false;
bool g_bRemoveUsers = false;
bool g_bFindDuplicates = false;
bool g_bPrintOnce = true;
bool g_bPopularityRank = false;
bool g_bCreateVerboseGraph = false;
int  g_minPPRVectorLenght = 2;
bool g_notRandom = false;
bool g_usePRPACK = false;
bool g_useGlobalPR = false;
bool g_compare = false;
bool g_specialNodes = false;
bool g_removeSpecialNodes = false;
bool g_findExcludeNodes = false;

std::string g_cmd;
std::multimap<float, std::string> g_sortedPPR;
std::map<std::string, int> g_addonsPopularityMap;
std::map<std::string, double> g_addonPageRankMap;
std::map<std::string, double> g_addonPRPositionMap;
std::map<std::string, double> g_companyTotalPageRankMap;
std::map<std::string, double> g_companyCurPageRankMap;
std::map<std::string, double> g_companyTotalPageRankCounterMap;
std::map<std::string, double> g_companyPPRSumMap;
std::map<std::string, double> g_companyPRSumMap;
std::map<std::string, double> g_companyPPRFreqSumMap;
std::map<std::string, double> g_companyPRFreqSumMap;
std::map<std::string, double> g_companyCompanyFreqSumMap;
std::multimap<std::string, std::string> g_fullPathNodes;
std::multimap<std::string, double> g_nodePPRMap;

std::map<std::string, std::string> g_companyKnownAddons;

std::vector<int> g_addons_vector;
std::vector<int> g_pos_vector;
std::vector<int> g_dups_vector;
std::vector<int> g_popularity_rank_vector;
std::vector<std::string> g_deletedNodes;
std::vector<long int> g_special_nodes_vector;
std::string g_special_nodes_names;
std::vector<std::string> competitorsNodesVec;
std::vector<std::string> g_excludeNodesVec;
std::vector<std::string> g_ignoreNodesVec = {"w-tbcore3.dll"};

FILE *ifile;
FILE *gfile;
FILE *ofile;
FILE *ofileAfterRemoval;
FILE *degreefile;
FILE *dupUsersfile;
FILE *recallfile;
FILE *excelfile;
FILE *popularityfile;
FILE *users_addons_map_file;

std::string ipath = R"(C:\addonsproject\test\testDup.txt)";
//auto ipath = R"(C:\addonsproject\graph_input_uniq.txt)";
std::string opath = R"(C:\addonsproject\PPR.txt)";
std::string recallpath = R"(C:\addonsproject\test\recall.txt)";
std::string excelpath;// = R"(C:\addonsproject\test\recall4Excel.xls)";
std::string globalePRpath;// = R"(C:\addonsproject\test\recall4Excel.xls)";
std::string opath2 = R"(C:\addonsproject\PPRAfterRemoval.txt)";
std::string degreePath = R"(RemovedAddonsDegree.txt)";
std::string nodesMapPath = R"(C:\addonsproject\nodesMap.txt)";
std::string comparePath;
std::string specialNodes;
std::string competitorsNodes;
int numberOfRuns = 200;
int totalNRuns = 0;

igraph_vector_t gtypes, vtypes, etypes;
igraph_strvector_t gnames, vnames, enames;

struct AddonData
{
  double pagerank;
  int popularity;
  double weighted_popularity;
};

std::map<std::string, AddonData> g_addonDataMap;

void PrintMemoryInfo(std::string str)
{
  HANDLE hProcess;
  PROCESS_MEMORY_COUNTERS pmc;

  // Print the process identifier.
  DWORD processID = GetCurrentProcessId();
  std::cout << str << std::endl;

  // Print information about the memory usage of the process.

  hProcess = OpenProcess(PROCESS_QUERY_INFORMATION |
    PROCESS_VM_READ,
    FALSE, processID);
  if (NULL == hProcess)
    return;

  if (GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc)))
  {
    printf("\tWorkingSetSize: %lu\n", pmc.WorkingSetSize >> 20);
  }

  CloseHandle(hProcess);
}

void replaceAll(std::string& str, const std::string& from, const std::string& to) {
  if (from.empty())
    return;
  size_t start_pos = 0;
  while ((start_pos = str.find(from, start_pos)) != std::string::npos) {
    str.replace(start_pos, from.length(), to);
    start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
  }
}

std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems) {
  std::stringstream ss(s);
  std::string item;
  while (std::getline(ss, item, delim)) {
    elems.push_back(item);
  }
  return elems;
}


std::vector<std::string> split(const std::string &s, char delim) {
  std::vector<std::string> elems;
  split(s, delim, elems);
  return elems;
}

bool is_number(const std::string& s)
{
  return !s.empty() && std::find_if(s.begin(),
    s.end(), [](char c) { return !std::isdigit(c); }) == s.end();
}

std::string nodeNamefromId(const igraph_t *g, FILE *f, int nodeId)
{
  //PrintMemoryInfo("Start nodeNamefromId");
  std::string name;
  if (g_bRemoveUsers || g_bRemoveTerms)
    g = &g_orig;

  name = VAS(g, STR(vnames, 0), nodeId);

  if (nodeId < 0)
    return "";

  //PrintMemoryInfo("End nodeNamefromId");
  return name;
}

int igraphNodeIDfromNodeMapID(const igraph_t *g, FILE *f, int NodeName)
{
  //PrintMemoryInfo("Start igraphNodeIDfromNodeMapID");
  long int i = -1;

  igraph_strvector_t svec;
  long int j;


  if (g_bRemoveUsers || g_bRemoveTerms)
    g = &g_orig;

  igraph_strvector_init(&svec, 0);
  igraph_cattribute_VASV(g, "name", igraph_vss_all(), &svec);


  for (i = 0; i < igraph_vcount(g); i++) {
    //const char *str = VAS(g, STR(vnames, 0), i);

    //std::string str1 = VAS(g, STR(vnames, 0), i);
    std::string strCurNodeName = STR(svec, i);
    if (is_number(strCurNodeName))
    {
      int CurNodeName = std::stoi(strCurNodeName);
      if (CurNodeName == NodeName)
      {
        break;
        //return i;
      }
    }
  }

  igraph_strvector_clear(&svec);
  igraph_strvector_destroy(&svec);

  return i;
}

int print_attributes(const igraph_t *g, FILE *f) {

  igraph_vector_t gtypes, vtypes, etypes;
  igraph_strvector_t gnames, vnames, enames;
  long int i;

  igraph_vector_t vec;
  igraph_strvector_t svec;
  long int j;

  igraph_vector_init(&gtypes, 0);
  igraph_vector_init(&vtypes, 0);
  igraph_vector_init(&etypes, 0);
  igraph_strvector_init(&gnames, 0);
  igraph_strvector_init(&vnames, 0);
  igraph_strvector_init(&enames, 0);

  igraph_cattribute_list(g, &gnames, &gtypes, &vnames, &vtypes,
    &enames, &etypes);

  /* Graph attributes */
  for (i = 0; i < igraph_strvector_size(&gnames); i++) {
    printf("%s=", STR(gnames, i));
    if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_NUMERIC) {
      igraph_real_printf(GAN(g, STR(gnames, i)));
      putchar(' ');
    }
    else {
      printf("\"%s\" ", GAS(g, STR(gnames, i)));
    }
  }
  printf("\n");

  for (i = 0; i < igraph_vcount(g); i++) {
    long int j;
    fprintf(f, "Vertex %li: ", i);
    for (j = 0; j < igraph_strvector_size(&vnames); j++) {
      fprintf(f, "%s=", STR(vnames, j));
      if (VECTOR(vtypes)[j] == IGRAPH_ATTRIBUTE_NUMERIC) {
        igraph_real_printf(VAN(g, STR(vnames, j), i));
        putchar(' ');
      }
      else {
        fprintf(f, "%s", VAS(g, STR(vnames, j), i));
      }
    }
    fprintf(f, "\n");
  }

  for (i = 0; i < igraph_ecount(g); i++) {
    long int j;
    fprintf(f, "Edge %li (%i-%i): ", i, (int)IGRAPH_FROM(g, i), (int)IGRAPH_TO(g, i));
    for (j = 0; j < igraph_strvector_size(&enames); j++) {
      fprintf(f, "%s=", STR(enames, j));
      if (VECTOR(etypes)[j] == IGRAPH_ATTRIBUTE_NUMERIC) {
        igraph_real_printf(EAN(g, STR(enames, j), i));
        putchar(' ');
      }
      else {
        fprintf(f, "\"%s\" ", EAS(g, STR(enames, j), i));
      }
    }
    fprintf(f, "\n");
  }

  ///* Check vector-based query functions */
  //igraph_vector_init(&vec, 0);
  //igraph_strvector_init(&svec, 0);
  //
  //for (j = 0; j<igraph_strvector_size(&vnames); j++) {
  //  if (VECTOR(vtypes)[j] == IGRAPH_ATTRIBUTE_NUMERIC) {
  //    igraph_cattribute_VANV(g, STR(vnames, j), igraph_vss_all(), &vec);
  //    for (i = 0; i<igraph_vcount(g); i++) {
  //      igraph_real_t num = VAN(g, STR(vnames, j), i);
  //      if (num != VECTOR(vec)[i] &&
  //        (!isnan(num) || !isnan(VECTOR(vec)[i]))) {
  //        exit(51);
  //      }
  //    }
  //  }
  //  else {
  //    igraph_cattribute_VASV(g, STR(vnames, j), igraph_vss_all(), &svec);
  //    for (i = 0; i<igraph_vcount(g); i++) {
  //      const char *str = VAS(g, STR(vnames, j), i);
  //      if (strcmp(str, STR(svec, i))) {
  //        exit(52);
  //      }
  //    }
  //  }
  //}
  //
  //for (j = 0; j<igraph_strvector_size(&enames); j++) {
  //  if (VECTOR(etypes)[j] == IGRAPH_ATTRIBUTE_NUMERIC) {
  //    igraph_cattribute_EANV(g, STR(enames, j),
  //      igraph_ess_all(IGRAPH_EDGEORDER_ID), &vec);
  //    for (i = 0; i<igraph_ecount(g); i++) {
  //      igraph_real_t num = EAN(g, STR(enames, j), i);
  //      if (num != VECTOR(vec)[i] &&
  //        (!isnan(num) || !isnan(VECTOR(vec)[i]))) {
  //        exit(53);
  //      }
  //    }
  //  }
  //  else {
  //    igraph_cattribute_EASV(g, STR(enames, j),
  //      igraph_ess_all(IGRAPH_EDGEORDER_ID), &svec);
  //    for (i = 0; i<igraph_ecount(g); i++) {
  //      const char *str = EAS(g, STR(enames, j), i);
  //      if (strcmp(str, STR(svec, i))) {
  //        exit(54);
  //      }
  //    }
  //  }
  //}
  //igraph_strvector_destroy(&svec);
  //igraph_vector_destroy(&vec);

  igraph_strvector_destroy(&enames);
  igraph_strvector_destroy(&vnames);
  igraph_strvector_destroy(&gnames);
  igraph_vector_destroy(&etypes);
  igraph_vector_destroy(&vtypes);
  igraph_vector_destroy(&gtypes);

  return 0;
}


void print_vector(igraph_vector_t *v, FILE *f) {
  long int i;
  for (i = 0; i < igraph_vector_size(v); i++) {
    float m = VECTOR(*v)[i] * 1;
    //printf("\"%s\" ", VAS(&g, STR(vnames, 0), 1));
    fprintf(f, " %g\n", m);

  }
  //igraph_vector_print(v);
  fprintf(f, "\n");
}

void print_nodes_connection_state(FILE *f, igraph_t *g, igraph_integer_t from, igraph_integer_t to)
{
  igraph_bool_t conn = 2;
  igraph_are_connected(g, (igraph_integer_t)from,
    (igraph_integer_t)to, &conn);

  if (conn == 1)
    std::cout << "node " << from << " IS connected to node " << to << std::endl;
  else if (conn == 0)
    std::cout << "node " << from << " NOT connected to node " << to << std::endl;
  else
    std::cout << "Error" << std::endl;
}

int countAddonPopularity(FILE *f, igraph_t *g, igraph_vs_t *vsAddonsReset, char* str)
{
  //fprintf(f, "%s\n", str);
  int popularity = 0;
  igraph_vit_t vit;
  igraph_vit_create(g, *vsAddonsReset, &vit);
  while (!IGRAPH_VIT_END(vit)) {
    std::string nodeName = nodeNamefromId(g, stdout, (long int)IGRAPH_VIT_GET(vit)).c_str();
    if (nodeName.at(0) == '1')
    {
      popularity++;
      //fprintf(f, "%li ", (long int)IGRAPH_VIT_GET(vit));
      //fprintf(f, "%s ", nodeName.c_str());
      //fprintf(f, " %s\n", g_Data.RowMap[nodeNamefromId(g, stdout, (long int)IGRAPH_VIT_GET(vit))].c_str());
      //fprintf(f, " %s\n", g_Data.RowMap[nodeNamefromId(g, stdout, (long int)IGRAPH_VIT_GET(vit))].c_str());
    }
    IGRAPH_VIT_NEXT(vit);
  }
  igraph_vit_destroy(&vit);

  return popularity;
}

std::vector<std::string> print_ppr_data(FILE *f, igraph_t *g, igraph_vs_t *vsAddonsReset, char* str)
{
  std::vector<std::string> pprVec;
  fprintf(f, "%s\n", str);
  igraph_vit_t vit;
  igraph_vit_create(g, *vsAddonsReset, &vit);
  std::vector<int> tmpvec;
  g_addons_vector.swap(tmpvec);
  fprintf(recallfile, "-----------ADDONS VECTOR-------------\n");
  while (!IGRAPH_VIT_END(vit)) {
    fprintf(f, "%li ", (long int)IGRAPH_VIT_GET(vit));
    fprintf(f, "%s ", nodeNamefromId(g, stdout, (long int)IGRAPH_VIT_GET(vit)).c_str());
    fprintf(f, " %s\n",g_Data.RowMap[nodeNamefromId(g, stdout, (long int)IGRAPH_VIT_GET(vit))].c_str());
    fprintf(recallfile, "%s %s\n", nodeNamefromId(g, stdout, (long int)IGRAPH_VIT_GET(vit)).c_str(), g_Data.RowMap[nodeNamefromId(g, stdout, (long int)IGRAPH_VIT_GET(vit))].c_str());
    g_addons_vector.push_back((int)IGRAPH_VIT_GET(vit));
    pprVec.push_back(g_Data.RowMap[nodeNamefromId(g, stdout, (long int)IGRAPH_VIT_GET(vit))].c_str());
    IGRAPH_VIT_NEXT(vit);
  }
  igraph_vit_destroy(&vit);
  return pprVec;
}

void print_ppr_vector(igraph_vector_t *v, FILE *f, igraph_strvector_t *vnames, igraph_t *g, char* str, igraph_vs_t *vsAddonsReset, bool bPrintRecall) 
{
  //PrintMemoryInfo("Start print_ppr_vector");
  long int i;
  igraph_vector_t v_sorted;
  igraph_vector_copy(&v_sorted, v);
  igraph_vector_sort(&v_sorted);
  igraph_integer_t size = 0;

  if (vsAddonsReset)
  {
    igraph_vs_size(g, vsAddonsReset, &size);
  }

  std::string our_name;
  std::string our_fullname;
  int addonPopularityRank = 0;
  float ourAddonPagerank = 0;
  std::vector<float> pr_vector;
  int numOfOrigAddonsGreater = 0;

  std::string uni = opath2; //= std::to_string(numberOfRuns);
  std::string tmps = "-";
  tmps += std::to_string(totalNRuns - numberOfRuns + 1);
  tmps += ".";
  replaceAll(uni, ".", tmps);

  //FILE *ofileAfterRemoval_temp = fopen(uni.c_str(), "w");
  int mult = 1000;

  std::vector<std::pair<float, std::string>> addon_pagerank_vec;
  std::vector<std::string> tmp_vec;

  for (i = 0; i < igraph_vector_size(v); i++) 
  {
    float pagerank = VECTOR(*v)[i] * mult;
    if (g_addonsOnly)
    {
      std::string nodeName = VAS(g, STR(*vnames, 0), i);
      if (nodeName.at(0) != '2' && nodeName.at(0) != '3' && nodeName.at(0) != '4')
      {
        continue;
      }
    }

    //fprintf(excelfile, " %g\t", pagerank);
    //fprintf(excelfile, "%s\t", VAS(g, STR(*vnames, 0), i));
    std::string name = VAS(g, STR(*vnames, 0), i);
    std::string addonFullName = g_Data.RowMap[name];
    //addonPopularityRank = g_addonsPopularityMap.find(our_name)->second;
    addonPopularityRank = g_addonsPopularityMap[our_name];
    if (!g_useGlobalPR && i == g_idVecToRemove)
    {
      long int pos = 0;
      ourAddonPagerank = pagerank;
      our_name = name;
      our_fullname = addonFullName;
      igraph_vector_binsearch(&v_sorted, VECTOR(*v)[i], &pos);
      addonFullName.insert(0, "YYY-");
      igraph_vector_destroy(&v_sorted);
    }
    else if (!g_useGlobalPR && std::find(g_addons_vector.begin(), g_addons_vector.end(), i) != g_addons_vector.end())
    {
      int pprVecPopularityRank = g_addonsPopularityMap[name];
      if (pprVecPopularityRank > addonPopularityRank)
      {
        addonPopularityRank--;
      }
      if (pagerank > ourAddonPagerank)
      {
        numOfOrigAddonsGreater++;
      }
      addonFullName.insert(0, "ZZZ-");
    }

    fprintf(excelfile, " %s\n", addonFullName.c_str());

    pr_vector.push_back(pagerank);
    std::string tmp = name;
    tmp += '\t';
    tmp += addonFullName;
    
    std::cmatch cm;
    if (!specialNodes.empty() && std::regex_match(addonFullName.c_str(), cm, std::regex(specialNodes.c_str())))
    {
      //std::cout << pagerank << " " << tmp << endl;
    }

    //addon_pagerank_vec.emplace_back(tmp, pagerank);
    addon_pagerank_vec.push_back(make_pair(pagerank,tmp));
    tmp_vec.push_back(tmp);
  }

  auto cmp = [](std::pair<float, std::string> const & a, std::pair<float, std::string> const & b)
  {
    int mult = 1000;
    //auto ret = a.first * mult != b.first * mult ? a.first * mult < b.first * mult : a.second.length() < b.second.length();
    if (a.first < b.first)
    {
      //if (a.second.find(specialNode) != std::string::npos)
      //{
      //  cout << "a*** " << a.second << " " << a.first << endl;
      //  cout << "b*** " << b.second << " " << b.first << endl;
      //  cout << "true" << endl;
      //}
      return true;
    }
    else
    {
      //if (a.second.find(specialNode) != std::string::npos)
      //{
      //  cout << "a*** " << a.second << " " << a.first << endl;
      //  cout << "b*** " << b.second << " " << b.first << endl;
      //  cout << "false" << endl;
      //}
      return false;
    }
  };

  std::sort(addon_pagerank_vec.begin(), addon_pagerank_vec.end(), cmp);
  if (!g_useGlobalPR)
  {
    fprintf(recallfile, "----------- TOP PAGE RANK-------------\n");
    for (auto it = addon_pagerank_vec.rbegin(); it != addon_pagerank_vec.rend(); it++)
    {
      fprintf(recallfile, " %.5g\t", it->first);
      fprintf(recallfile, " %s\n", it->second.c_str());
    }

    fprintf(recallfile, "-----------------------------------------\n");
  }
  else
  {
    fprintf(recallfile, "----------- TOP PAGE RANK-------------\n");
    for (auto it = addon_pagerank_vec.rbegin(); it != addon_pagerank_vec.rend();it++)
    {
      //if (it->second.find(specialNode) != std::string::npos)
      //{
      //  cout << "**** " << it->second << endl;
      //}

      //auto it2 = tmp_vec.back();
      //tmp_vec.pop_back();

      fprintf(recallfile, " %.5g\t", it->first);
      fprintf(recallfile, " %s\n", it->second.c_str());
      //std::string str = it->second;
      //if (str.find(specialNode) != std::string::npos)
      //{
      //  cout << "* " << str << endl;
      //}
      //if (it2.find(specialNode) != std::string::npos)
      //{
      //  cout << "** " << it2 << endl;
      //}

    }

    fprintf(recallfile, "-----------------------------------------\n");
  }

  //fclose(ofileAfterRemoval_temp);

  // print content:
  if ( bPrintRecall)
  {
    std::string cmdToReproduce = g_cmd;
    replaceAll(cmdToReproduce, "-u rnd", "-u " + std::to_string(g_uid));
    if (g_idVecToRemove != -1)
      replaceAll(cmdToReproduce, "-a rnd", "-a " + nodeNamefromId(g, stdout, g_idVecToRemove));

    std::sort(pr_vector.begin(), pr_vector.end(), std::greater<float>());
    auto x = std::find(pr_vector.begin(), pr_vector.end(), ourAddonPagerank);
    auto x2 = std::find(g_popularity_rank_vector.begin(), g_popularity_rank_vector.end(), addonPopularityRank);

    std::cout << "\nOur addon " << our_name << " current pagerank " << ourAddonPagerank << " position " << std::distance(pr_vector.begin(), x) + 1 << " out of " << pr_vector.size() << " addons." << std::endl;
    int dist = std::distance(pr_vector.begin(), x) + 1;
    int apopulRank = std::distance(g_popularity_rank_vector.begin(), x2) + 1;
    //count how many of the 
    int effectiveRank = dist - numOfOrigAddonsGreater;
    g_pos_vector.push_back(effectiveRank);
    
    if (g_bPrintOnce)
    {
      g_bPrintOnce = false;

      fprintf(excelfile, "Rank\tIsDup\tPPR_length\tPageRank\tPop Rank\tFull name\tNode name\tUser ID\tCMD to reproduce\n");
    }
    int bIsDup = 0;
    if (std::find(g_dups_vector.begin(), g_dups_vector.end(), g_uid) != g_dups_vector.end())
    {
      bIsDup = 1;
    }
    //fprintf(recallfile, "-----------FINAL SCORES-------------\nRank\tPPR_length\tPageRank\tPop Rank\tFull name\tNode name\tUser ID\n", effectiveRank, size, ourAddonPagerank, our_fullname.c_str(), our_name.c_str(), g_uid, cmdToReproduce.c_str());
    fprintf(recallfile, "-----------FINAL SCORES-------------\nRank(%d)\tIsDup(%d)\tPPR_length(%d)\tPageRank(%g)\tNode ID(%s)\tPop Rank(%d)\t%s\tUser_Node_Id(%d)\n", effectiveRank, bIsDup, size + 1, ourAddonPagerank, our_name.c_str(), apopulRank, our_fullname.c_str(), g_uid);
    fprintf(recallfile, "\n################################################################################################################\n\n");

    fprintf(excelfile, "%d\t%d\t%d\t%g\t%d\t%s\t%s\t%d\t%s\n", effectiveRank, bIsDup, size, ourAddonPagerank, apopulRank, our_fullname.c_str(), our_name.c_str(), g_uid, cmdToReproduce.c_str());
    //fprintf(stdout,     "%d\t%d\t%g\t%d\t%s\t%s\t%d\t%s\n", effectiveRank, size, ourAddonPagerank, apopulRank,our_fullname.c_str(), our_name.c_str(), g_uid, cmdToReproduce.c_str());
    fprintf(users_addons_map_file, "%d\t%s\n", g_uid, our_name.c_str());
  }
}


bool readNodesMap(std::unordered_map<std::string, std::string> &row, std::string fileName, bool readExtra = false)
{
  std::ifstream filehandle(fileName.c_str());

  std::string line = "";
  while (std::getline(filehandle, line))
  {
    std::stringstream line_ss(line);
    std::string column = "";
    unsigned int index = 0;
    std::string nodeId;
    std::string nodeName;
    while (std::getline(line_ss, column, '\t'))
    {
      if (index == 0)
      {
        nodeId = column;
      }
      if (index == 1)
      {
        nodeName = column;
      }
      index++;
    }
    row[nodeId] = nodeName;
    if (readExtra)
    {
      std::vector<std::string> NodesVec;
      NodesVec = split(nodeName, '\\');

      //insert only if there is no the exactly same key/value pair
      string keyName = NodesVec.back();
      if (!keyName.empty())
      {
        char tailchar = keyName.at(keyName.length() - 1);
        while (!keyName.empty() && tailchar == '_')
        {
          //std::cout << keyName << endl;
          keyName = keyName.substr(0, keyName.length() - 1);
          if (!keyName.empty())
            tailchar = keyName.at(keyName.length() - 1);
        }
        //std::cout << keyName << endl;
      }
      NodesVec.pop_back();
      NodesVec.push_back(keyName);

      auto fullnameRange = g_fullPathNodes.equal_range(keyName);
      std::string fullNameFullPath;
      bool bInsert = true;

      for (auto itinner = fullnameRange.first; itinner != fullnameRange.second; ++itinner)
      {
        fullNameFullPath = itinner->second;
        if (fullNameFullPath.find(nodeName) != string::npos)
        {
          std::cout << "Skipping duplicate entry " << nodeName << endl;
          bInsert = false;
        }
      }

      if (bInsert)
      {
        //remove trailing __
        if (!nodeName.empty())
        {
          char tailchar = nodeName.at(nodeName.length() - 1);
          while (!nodeName.empty() && tailchar == '_')
          {
            //std::cout << nodeName << endl;
            nodeName = nodeName.substr(0, nodeName.length() - 1);
            tailchar = nodeName.at(nodeName.length() - 1);
          }
          //std::cout << nodeName << endl;
        }
        g_fullPathNodes.emplace(NodesVec.back(), nodeName);
      }
    }

    //FILE *temp;
    //std::string orig_recallpath = recallpath;
    //temp = fopen(orig_recallpath.insert(orig_recallpath.length() - 4, "NodesPath").c_str(), "w");
    //for (auto it = g_fullPathNodes.begin();
    //  it != g_fullPathNodes.end();
    //  it = g_fullPathNodes.upper_bound(it->first)
    //  )
    //{
    //  auto range = g_fullPathNodes.equal_range(it->first);
    //  //std::cout << it->first << std::endl;

    //  //cout << it->first << ' ' << it->second << endl;
    //  for (auto itinner = range.first; itinner != range.second; ++itinner)
    //  {
    //    //std::cout << "Insert: "<<it->first << " " << it->second;
    //    fprintf(temp, "%s\t%s\n", itinner->first.c_str(), itinner->second.c_str());
    //  }
    //}

    //fflush(temp);
    //fclose(temp);
    //return 0;
    }

  return true;
}

void readExcludeFile(std::string fileName)
{
  std::cout << "Excluding nodes from file " << fileName << endl;
  std::ifstream filehandle(fileName.c_str());

  std::string line = "";

  while (std::getline(filehandle, line))
  {
    std::stringstream line_ss(line);
    std::string column = "";
    unsigned int index = 0;
    std::string nodeRank;
    double dnodeRank;
    std::string nodeId;
    std::string nodeName;
    while (std::getline(line_ss, column, '\t'))
    {
      g_excludeNodesVec.push_back(column);
    }
  }

  std::sort(g_excludeNodesVec.begin(), g_excludeNodesVec.end());

  return;
}

void read_writeRawPRFile(std::string fileName)
{
  std::ifstream filehandle(fileName.c_str());

  FILE *temp1;
  std::string orig_recallpath = recallpath;
  temp1 = fopen(orig_recallpath.insert(orig_recallpath.length() - 4, "PRExcluded").c_str(), "w");

  std::string line = "";
  int position = 1;
  while (std::getline(filehandle, line))
  {
    std::stringstream line_ss(line);
    std::string column = "";
    unsigned int index = 0;
    std::string nodeRank;
    double dnodeRank;
    std::string nodeId;
    std::string nodename;
    while (std::getline(line_ss, column, '\t'))
    {
      if (index == 0)
      {
        nodeRank = column;
        dnodeRank = atof(nodeRank.c_str());
      }
      if (index == 2)
      {
        nodename = column;
        //if (std::find(g_excludeNodesVec.begin(), g_excludeNodesVec.end(), nodeName) != g_excludeNodesVec.end())
        auto cmp = [](std::string const & x, string d) -> bool
        { return x.compare(d) == 0; };
        //if (std::lower_bound(g_excludeNodesVec.begin(), g_excludeNodesVec.end(), nodeName,cmp) != g_excludeNodesVec.end())
        if (std::binary_search(g_excludeNodesVec.begin(), g_excludeNodesVec.end(), nodename))
        {
          //std::cout << "PR Excluding " << nodename << endl;
          position--;
        }
        else
        {
          //std::string s = std::to_string(position);
          //replaceAll(line, nodeRank, s);
          fprintf(temp1, "%s\n", line.c_str());
        }
      }
      index++;
    }
    position++;
  }

  fclose(temp1);

  return;
}

void read_writeRawComparisonFile(std::string fileName)
{
  std::ifstream filehandle(fileName.c_str());

  std::cout << "Re-numbering " << fileName << endl;
  FILE *temp1;
  std::string orig_recallpath = recallpath;
  temp1 = fopen(orig_recallpath.insert(orig_recallpath.length() - 4, "RawExcluded").c_str(), "w");
  if (temp1 == NULL)
  {
    std::cout << "Error opening read_writeRawCompFile file" << endl;
    exit(0);
  }
  fprintf(temp1, "%s\t%-8s\t  %-8s\t%-8s\t%-8s\t%s\t%s\t%s\t%s\n", "PPR Rank", "PR Rank", "Diff", "PPRScore", "PRScore", "Frequency", "Node Id", "Node name", "Company name");

  std::string line = "";
  bool FirstLine = true;
  int position = 1;
  while (std::getline(filehandle, line))
  {
    if (FirstLine)
    {
      FirstLine = false;
      continue;
    }
    std::stringstream line_ss(line);
    std::string column = "";
    unsigned int index = 0;
    std::string nodeRank;
    double dnodeRank;
    std::string nodeId;
    std::string nodeName;
    int PPRRank, PRRank, Diff, Frequency;
    double PPRScore, PRScore;
    string nodeid, nodename, companyname;
    while (std::getline(line_ss, column, '\t'))
    {
      if (index == 0)
      {
        PPRRank = atof(column.c_str());
      }
      if (index == 1)
      {
        PRRank = atof(column.c_str());
      }
      if (index == 2)
      {
        //PRRank = atof(column.c_str());
        Diff = PPRRank - PRRank;
      }
      if (index == 3)
      {
        PPRScore = atof(column.c_str());
      }
      if (index == 4)
      {
        PRScore = atof(column.c_str());
      }
      if (index == 5)
      {
        Frequency = atof(column.c_str());
      }
      if (index == 6)
      {
        nodeid = column;
      }
      if (index == 7)
      {
        nodename = column;
      }
      if (index == 8)
      {
        companyname = column;
        //if (std::find(g_excludeNodesVec.begin(), g_excludeNodesVec.end(), nodename) != g_excludeNodesVec.end())
        auto cmp = [](std::string const & x, string d) -> bool
        { return x.compare(d) == 0; };
        //if (std::lower_bound(g_excludeNodesVec.begin(), g_excludeNodesVec.end(), nodeName,cmp) != g_excludeNodesVec.end())
        if (std::binary_search(g_excludeNodesVec.begin(), g_excludeNodesVec.end(), nodename))
        {
          //std::cout << "RAW Excluding " << nodename << endl;
          position--;
        }
        else
        {
          PPRRank = position;
          Diff = PPRRank - PRRank;
          fprintf(temp1, "%d\t%-8d\t  %-8d\t%-8f\t%-8f\t%d\t%s\t%s\t%s\n", PPRRank, PRRank, Diff, PPRScore, PRScore, Frequency, nodeid.c_str(), nodename.c_str(), companyname.c_str());
        }
        g_nodePPRMap.emplace(nodename, PPRRank);
      }
      index++;
    }
    position++;
  }

  fclose(temp1);

  std::cout << "PPR nodes map size: " << g_nodePPRMap.size() << endl;
  for (auto it : g_nodePPRMap)
  {
    if (g_nodePPRMap.count(it.first) > 1)
      std::cout << "PPR nodes map: " << it.first << " appears " << g_nodePPRMap.count(it.first) << endl;
  }

  return;
}
void read_writeRawComparisonCompetitorsFile(std::string fileName)
{
  std::ifstream filehandle(fileName.c_str());

  std::cout << "Re-numbering " << fileName << endl;
  FILE *temp1;
  std::string orig_recallpath = recallpath;
  temp1 = fopen(orig_recallpath.insert(orig_recallpath.length() - 4, "CompetitorsRawExcluded").c_str(), "w");
  if (temp1 == NULL)
  {
    std::cout << "Error opening read_writeRawComparisonCompetitorsFile file" << endl;
    exit(0);
  }
  fprintf(temp1, "%s\t%-8s\t  %-8s\t%-8s\t%-8s\t%s\t%s\t%s\t%s\n", "PPR Rank", "PR Rank", "Diff", "PPRScore", "PRScore", "Frequency", "Node Id", "Node name", "Company name");

  std::string line = "";
  bool FirstLine = true;
  int position = 1;
  while (std::getline(filehandle, line))
  {
    if (FirstLine)
    {
      FirstLine = false;
      continue;
    }
    std::stringstream line_ss(line);
    std::string column = "";
    unsigned int index = 0;
    std::string nodeRank;
    double dnodeRank;
    std::string nodeId;
    std::string nodeName;
    int PPRRank, PRRank, Diff, Frequency;
    double PPRScore, PRScore;
    string nodeid, nodename, companyname;
    while (std::getline(line_ss, column, '\t'))
    {
      if (index == 0)
      {
        PPRRank = atof(column.c_str());
      }
      if (index == 1)
      {
        PRRank = atof(column.c_str());
      }
      if (index == 2)
      {
        //PRRank = atof(column.c_str());
        Diff = PPRRank - PRRank;
      }
      if (index == 3)
      {
        PPRScore = atof(column.c_str());
      }
      if (index == 4)
      {
        PRScore = atof(column.c_str());
      }
      if (index == 5)
      {
        Frequency = atof(column.c_str());
      }
      if (index == 6)
      {
        nodeid = column;
      }
      if (index == 7)
      {
        nodename = column;
      }
      if (index == 8)
      {
        companyname = column;
      }
      index++;
    }

    auto f = g_nodePPRMap.find(nodename);
    if (f != g_nodePPRMap.end())
      PPRRank = f->second;

    Diff = PPRRank - PRRank;
    if (PPRRank != 0)
      fprintf(temp1, "%d\t%-8d\t  %-8d\t%-8f\t%-8f\t%d\t%s\t%s\t%s\n", PPRRank, PRRank, Diff, PPRScore, PRScore, Frequency, nodeid.c_str(), nodename.c_str(), companyname.c_str());


    position++;
  }

  fclose(temp1);

  std::cout << "PPR nodes map size: " << g_nodePPRMap.size() << endl;
  for (auto it : g_nodePPRMap)
  {
    if (g_nodePPRMap.count(it.first) > 1)
      std::cout << "PPR nodes map: " << it.first << " appears " << g_nodePPRMap.count(it.first) << endl;
  }

  return;
}
//void readRawCompFile(std::string fileName)
//{
//  std::ifstream filehandle(fileName.c_str());
//  
//  FILE *temp1;
//  std::string orig_recallpath = recallpath;
//  temp1 = fopen(orig_recallpath.insert(orig_recallpath.length() - 4, "RawExcluded").c_str(), "w");
//  fprintf(temp1, "%s\t%-8s\t  %-8s\t%-8s %-8s\t%s\t%s\t%s\t%s\n", "PPR Rank", "PR Rank", "Diff", "PPRScore", "PRScore", "Frequency", "Node Id", "Node name", "Company name");
//
//  std::string line = "";
//  bool FirstLine = true;
//  int position = 1;
//  while (std::getline(filehandle, line))
//  {
//    if (FirstLine)
//    {
//      FirstLine = false;
//      continue;
//    }
//    std::stringstream line_ss(line);
//    std::string column = "";
//    unsigned int index = 0;
//    std::string nodeRank;
//    double dnodeRank;
//    std::string nodeId;
//    std::string nodeName;
//    while (std::getline(line_ss, column, '\t'))
//    {
//      if (index == 0)
//      {
//        nodeRank = column;
//        dnodeRank = atof(nodeRank.c_str());
//      }
//      if (index == 7)
//      {
//        nodeName = column;
//        if (std::find(g_excludeNodesVec.begin(), g_excludeNodesVec.end(), nodeName) != g_excludeNodesVec.end())
//        {
//          std::cout << "Excluding " << nodeName << endl;
//          position--;
//        }
//        else
//        {
//          
//          std::string s = std::to_string(position);
//          s += "       ";
//          //sprintf(s, "%-8d", position);
//          replaceAll(line, nodeRank, s);
//          fprintf(temp1, "%s\n", line.c_str());
//        }
//      }
//      index++;
//    }
//    position++;
//  }
//
//  fclose(temp1);
//
//  return;
//}

std::vector<pair<string, double>> readNodesPR(std::string fileName)
{
  std::ifstream filehandle(fileName.c_str());
  std::vector<pair<string, double>> vec;
  vec.reserve(666000);
  std::vector<pair<double,string>> rank_vec;
  rank_vec.reserve(666000);

  std::string line = "";

  int position = 1;
  while (std::getline(filehandle, line))
  {
    std::stringstream line_ss(line);
    std::string column = "";
    unsigned int index = 0;
    std::string nodeRank;
    double dnodeRank;
    std::string nodeId;
    std::string nodeName;
    while (std::getline(line_ss, column, '\t'))
    {
      if (index == 0)
      {
        nodeRank = column;
        dnodeRank = atof(nodeRank.c_str());
      }
      if (index == 1)
      {
        nodeId = column;
      }
      if (index == 2)
      {
        nodeName = column;
      }
      index++;
    }
    vec.push_back(std::make_pair(nodeName, position));
    rank_vec.push_back(std::make_pair(position,nodeName));
    g_addonPageRankMap[nodeName] = dnodeRank;
    g_addonPRPositionMap[nodeName] = position;
    //std::cout << nodeName << " : " << position << endl;
    position++;
  }

  std::sort(vec.begin(), vec.end());
  //std::sort(rank_vec.begin(), rank_vec.end());
  //for (auto it : rank_vec)
  //{
  //}

  return vec;
}

int readGraphMap(std::string fileName)
{
  std::multimap<std::string, std::string> myumm;
  std::multimap<std::string, std::string> final_mm;
  std::ifstream filehandle(fileName.c_str());
  int totalDups = 0;

  std::string line = "";
  fprintf(dupUsersfile, "#Users\t#Addons\n");
  while (std::getline(filehandle, line))
  {
    std::stringstream line_ss(line);
    std::string column = "";
    unsigned int index = 0;
    std::string nodeS;
    std::string nodeD;
    while (std::getline(line_ss, column, '\t'))
    {
      if (index == 0)
      {
        if (column.at(0) != '1')
        {
          continue;
        }
        nodeS = column;
      }
      if (index == 1)
      {
        nodeD = column;
      }
      index++;
    }
    myumm.emplace(nodeS, nodeD);
  }

  for (auto it = myumm.begin();
    it != myumm.end();
    it = myumm.upper_bound(it->first)
    )
  {
    auto range = myumm.equal_range(it->first);
    //std::cout << it->first << std::endl;

    //cout << it->first << ' ' << it->second << endl;
    std::string alladdons;
    for (auto itinner = range.first; itinner != range.second; ++itinner)
    {
      //std::cout << " " << it->second;
      alladdons += itinner->second;
      alladdons += ";";
    }

    final_mm.emplace(alladdons, it->first);
    //std::cout << std::endl;
  }

  for (auto it = final_mm.begin();
    it != final_mm.end();
    it = final_mm.upper_bound(it->first)
    )
  {
    //auto range = myumm.equal_range(it->first);
    auto itupper = final_mm.upper_bound(it->first);
    auto itlower = final_mm.lower_bound(it->first);
    int dist = std::distance(itlower,itupper);
    //std::cout << dist << std::endl;
    if (dist > 1)
    {
      totalDups++;
      auto range = final_mm.equal_range(it->first);
      //std::cout << dist << '\t' << it->first << '\t' << std::endl;
      size_t n = std::count(it->first.begin(), it->first.end(), ';');
      fprintf(dupUsersfile, "%d\t%d\t%s\t", dist, n, it->first.c_str());
      for (auto itinner = range.first; itinner != range.second; ++itinner)
      {
        //std::cout << '\t' << itinner->second;
        fprintf(dupUsersfile, "%s %s\t", itinner->second.c_str(), g_Data.RowMap[itinner->second].c_str());
        g_dups_vector.push_back(std::stoi(itinner->second));
      }
      fprintf(dupUsersfile, "\n");
      
      //std::cout << std::endl;
    }
  }


  return totalDups;
}

bool readUserAddonsMap(std::vector<std::pair<std::string, std::string> > &row, std::string fileName)
{
  std::ifstream filehandle(fileName.c_str());

  std::string line = "";
  while (std::getline(filehandle, line))
  {
    std::stringstream line_ss(line);
    std::string column = "";
    unsigned int index = 0;
    std::string nodeId;
    std::string nodeName;
    while (std::getline(line_ss, column, '\t'))
    {
      if (index == 0)
      {
        nodeId = column;
      }
      if (index == 1)
      {
        nodeName = column;
      }
      index++;
    }
    
    row.emplace_back(nodeId, nodeName);
  }
  return false;
}

void print_and_destroy_cliques(igraph_vector_ptr_t *cliques) {
  int i;
  for (i = 0; i < igraph_vector_ptr_size(cliques); i++) {
    igraph_vector_t *v = (igraph_vector_t *)VECTOR(*cliques)[i];
    igraph_vector_print(v);
    igraph_vector_destroy(v);
    igraph_free(v);
  }
}

int pickRandomNumber(int a, int b)
{
  // construct a trivial random generator engine from a time-based seed:
  unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
  std::default_random_engine generator(seed);
  std::uniform_int_distribution<int> distribution(a, b - 1);
  return distribution(generator);
}

int pickRandomNumberNotEqual(int a, int b, std::vector<std::string> vec, igraph_t *g)
{
  //PrintMemoryInfo("Start pickRandomNumberNotEqual");
  int rnd = 0;
  std::string strRnd;
  int addonsNumber = 0;
  do
  {
    // construct a trivial random generator engine from a time-based seed:
    unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
    std::default_random_engine generator(seed);
    std::uniform_int_distribution<int> distribution(a, b - 1);
    rnd = distribution(generator);

    strRnd = std::to_string(rnd);
    //find all the nodes connected to uid --> all it's addons
    if (std::find(vec.begin(), vec.end(), strRnd) != vec.end())
      continue;

    int vid = igraphNodeIDfromNodeMapID(g, stdout, rnd);
    igraph_vs_t vsUid;
    igraph_vs_1(&vsUid, vid);
    igraph_vector_ptr_t vptrAddons;
    igraph_vector_ptr_init(&vptrAddons, 0);

    igraph_neighborhood(g, &vptrAddons, vsUid, 1, IGRAPH_ALL);
    igraph_vector_t *vAddons = (igraph_vector_t *)VECTOR(vptrAddons)[0];
    //remove first element since it is the uid node itself
    igraph_vector_remove(vAddons, 0);
    igraph_vs_t vsAddonsReset;
    igraph_vs_vector_copy(&vsAddonsReset, vAddons);

    addonsNumber = igraph_vector_size(vAddons);
    if (addonsNumber < g_minPPRVectorLenght)
      std::cout << "Only " << addonsNumber << " addons, continue looking...\n";
    else
      std::cout << "PPR length " << addonsNumber << " addons.\n";

    igraph_vector_ptr_destroy_all(&vptrAddons);

  } while (std::find(vec.begin(), vec.end(), strRnd) != vec.end() || addonsNumber < g_minPPRVectorLenght);

  //PrintMemoryInfo("End pickRandomNumberNotEqual");

  return rnd;
}

int igraph_degree_1(const igraph_t *igraph, int vertex) {
  igraph_real_t v;
  igraph_vector_t vp;
  igraph_vector_view(&vp, &v, 1);
  if (g_bRemoveUsers || g_bRemoveTerms)
    igraph = &g_orig;
  igraph_degree(igraph, &vp, igraph_vss_1(vertex), IGRAPH_ALL, IGRAPH_LOOPS);
  return v;
}

void printAllNodesDegrees(FILE *f, igraph_t *g)
{
  igraph_vector_t types;
  igraph_vector_init(&types, 0);
  igraph_degree(g, &types, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS);

  igraph_vector_print(&types);
  for (int i = 0; i < igraph_vector_size(&types); i++) {
    int nodeDegree = VECTOR(types)[i];
    std::cout << nodeNamefromId(g, f, i) << " degree " << nodeDegree << std::endl;
  }
}

std::vector<std::string> removeNodes(FILE *f, igraph_t *g, int maxDegree, int maxNodesNum, igraph_strvector_t *vnames)
{
  //PrintMemoryInfo("Start removeNodes");
  std::vector<std::string> deletedNodes;
  //if (maxDegree < 0 && maxNodesNum < 0)
  //  return deletedNodes;
  int numlowfreqnodes = 0;
  int numhighfreqnodes = 0;

  int minDegree = -1;
  igraph_vector_t sortedDegrees;
  igraph_vector_init(&sortedDegrees, 0);
  igraph_degree(g, &sortedDegrees, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS);
  igraph_vector_sort(&sortedDegrees);
  igraph_vector_reverse(&sortedDegrees);
#ifdef _MY_DEBUG
  igraph_vector_print(&sortedDegrees);
#endif // DEBUG
  if (maxNodesNum > 0 && maxNodesNum - 1 < igraph_vector_size(&sortedDegrees))
  {
    //find the degree of maxNodesNum's element
    minDegree = VECTOR(sortedDegrees)[maxNodesNum - 1];
    std::cout << "Min degree of not deleted nodes " << minDegree << std::endl;
  }

  igraph_vector_t types;
  igraph_vector_init(&types, 0);
  igraph_degree(g, &types, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS);
#ifdef _MY_DEBUG
  printAllNodesDegrees(f, g);
#endif // DEBUG

  igraph_vector_t toDelete;
  igraph_vector_init(&toDelete, 0);
  int removed = 0;
  //igraph_vector_print(&types);

  FILE *ExcludeTargetNodesFile;
  std::string orig_recallpath = recallpath;
  if (g_findExcludeNodes)
    ExcludeTargetNodesFile = fopen(orig_recallpath.insert(orig_recallpath.length() - 4, "ExcludeTargetNodes").c_str(), "w");
  else
    ExcludeTargetNodesFile = fopen(orig_recallpath.insert(orig_recallpath.length() - 4, "TempExTargetNodes").c_str(), "w");

  for (int i = 0; i < igraph_vector_size(&types); i++) {

    int nodeDegree = VECTOR(types)[i];
    std::string nodeName = VAS(g, STR(*vnames, 0), i);

    //if addon
    std::string fullName = g_Data.RowMap[nodeName];
    std::cmatch cm;
    if (g_specialNodes && (std::regex_match(fullName.c_str(), cm, std::regex(specialNodes.c_str()))
      || g_companyKnownAddons[fullName].compare(specialNodes) == 0))// && fullName.at(0) == 'w')
    {
      if (std::find(g_ignoreNodesVec.begin(), g_ignoreNodesVec.end(), fullName) != g_ignoreNodesVec.end())
      {
        std::cout << "Ignoring: " << fullName << ", node ID " << i << ", degree " << nodeDegree << endl;
      }
      else
      {
        int vid = igraphNodeIDfromNodeMapID(g, stdout, std::stoi(nodeName));
        std::cout << "Special node: " << fullName << " " << nodeName << ", node ID " << i << ", degree " << nodeDegree << endl;
        g_special_nodes_vector.push_back(i);
        g_special_nodes_names += fullName;
        g_special_nodes_names += " , ";
        fprintf(ExcludeTargetNodesFile, "%s\n", fullName.c_str());
      }
    }
    else if (g_specialNodes)
    {
      string correctfullName = fullName.substr(2);
      //if (correctfullName.find("tbmyba.dll") != string::npos)
      //{
      //  std::cout << "Key " << correctfullName << endl;
      //}
      //try to find if the node has some attribute in the path
      //check if exists in multimap of full paths
      auto fullnameRange = g_fullPathNodes.equal_range(correctfullName);
      std::string fullNameFullPath;
      for (auto itinner = fullnameRange.first; itinner != fullnameRange.second; ++itinner)
      {
        fullNameFullPath = itinner->second;
        //if (correctfullName.find("tbmyba.dll__") != string::npos)
        //{
        //  std::cout << "Full path " << fullNameFullPath << endl;
        //}
        std::cmatch cm;
        if (std::regex_match(fullNameFullPath.c_str(), cm, std::regex(specialNodes.c_str())))
        {
          if (std::find(g_ignoreNodesVec.begin(), g_ignoreNodesVec.end(), fullName) != g_ignoreNodesVec.end())
          {
            std::cout << "Ignoring: " << fullName << " " << fullNameFullPath << ", node ID " << i << ", degree " << nodeDegree << endl;
          }
          else
          {
            int vid = igraphNodeIDfromNodeMapID(g, stdout, std::stoi(nodeName));
            std::cout << "Special node Extra: " << fullName << " " << fullNameFullPath << ", node ID " << i << ", degree " << nodeDegree << endl;
            g_special_nodes_vector.push_back(i);
            g_special_nodes_names += fullName;
            g_special_nodes_names += " , ";
            fprintf(ExcludeTargetNodesFile, "%s\n", fullName.c_str());
          }
        }
      }
    }
    //addons popularity
    //if (std::regex_match(fullName.c_str(), cm, std::regex(competitorsNodes.c_str())) && fullName.at(0) == 'a')
    {
      //std::cout << fullName << "\t" << nodeDegree << endl;
      g_addonDataMap[fullName].popularity = nodeDegree;
      if (nodeName.at(0) == '2' || nodeName.at(0) == '3' || nodeName.at(0) == '4' || nodeName.at(0) == '5' || nodeName.at(0) == '6')
      {
        if (nodeDegree < 50)
        {
          numlowfreqnodes++;
        }
        else
        {
          numhighfreqnodes++;
        }
      }
      //int vid = igraphNodeIDfromNodeMapID(g, stdout, std::stoi(nodeName));
      //std::cout << vid << endl;
    }

    if (nodeName.at(0) == '2' || nodeName.at(0) == '3' || nodeName.at(0) == '4')
    {
      igraph_vector_t vNeighbors;
      igraph_vector_init(&vNeighbors, 0);
      igraph_neighbors(g, &vNeighbors, i, IGRAPH_ALL);

      igraph_vs_t vsNeighbors;
      std::string str = "Neighbors of ";
      str += nodeName;
      igraph_vs_vector_copy(&vsNeighbors, &vNeighbors);
#ifdef _MY_DEBUG
      print_ppr_data(stdout, g, &vsNeighbors, (char*)str.c_str());
#endif
      int popular = countAddonPopularity(f, g, &vsNeighbors, "");
      g_addonsPopularityMap.emplace(nodeName, popular);
      g_popularity_rank_vector.push_back(popular);

      igraph_vector_destroy(&vNeighbors);
      igraph_vs_destroy(&vsNeighbors);
    }

    if (maxDegree > 0 && nodeDegree > maxDegree)
    {
      //if we want to remove only maxNodesNum number of nodes, cut according to minDegree, calculated above
      if (minDegree > 0 && nodeDegree < minDegree)
        continue;

      igraph_vector_push_back(&toDelete, i);
      //std::cout << "Will delete " << nodeNamefromId(g, f, i) << " degree " << nodeDegree << " " << g_Data.RowMap[nodeNamefromId(g, f, i)] << std::endl;
      fprintf(f, "Will delete %s degree %d %s\n", nodeNamefromId(g, f, i).c_str(), nodeDegree, g_Data.RowMap[nodeNamefromId(g, f, i)].c_str());
      deletedNodes.push_back(nodeNamefromId(g, f, i));
      removed++;
    }
    else if (g_bRemoveTerms && 
      (nodeName.at(0) == '5' || nodeName.at(0) == '6'))
    {

      std::string fullName = g_Data.RowMap[nodeName];
      if (g_specialNodes && (std::regex_match(fullName.c_str(), cm, std::regex(specialNodes.c_str())) || g_companyKnownAddons[fullName].compare(specialNodes) == 0)
        && fullName.at(0) == 'w')
      {
        igraph_vector_push_back(&toDelete, i);
        std::cout << "Will delete " << nodeNamefromId(g, f, i) << " degree " << nodeDegree << " " << g_Data.RowMap[nodeNamefromId(g, f, i)] << std::endl;
        fprintf(f, "Will delete %s degree %d %s\n", nodeNamefromId(g, f, i).c_str(), nodeDegree, g_Data.RowMap[nodeNamefromId(g, f, i)].c_str());
        deletedNodes.push_back(nodeNamefromId(g, f, i));
        removed++;
        igraph_vector_t neighbor_edges;
        igraph_vector_init(&neighbor_edges, 0);
        igraph_incident(g, &neighbor_edges, i, IGRAPH_ALL);
        igraph_vector_append(&toDelete, &neighbor_edges);
      }
    }
    else if (g_bRemoveUsers &&
      (nodeName.at(0) == '1'))
    {
      //igraph_vector_push_back(&toDelete, i);
      //std::cout << "Will delete " << nodeNamefromId(g, f, i) << " degree " << nodeDegree << " " << g_Data.RowMap[nodeNamefromId(g, f, i)] << std::endl;
      fprintf(f, "Will delete %s degree %d %s\n", nodeNamefromId(g, f, i).c_str(), nodeDegree, g_Data.RowMap[nodeNamefromId(g, f, i)].c_str());
      deletedNodes.push_back(nodeNamefromId(g, f, i));
      removed++;
      igraph_vector_t neighbor_edges;
      igraph_vector_init(&neighbor_edges, 0);
      igraph_incident(g, &neighbor_edges, i, IGRAPH_ALL);
      igraph_vector_append(&toDelete, &neighbor_edges);
    }
    else if (g_removeSpecialNodes &&
      (nodeName.at(0) == '2' || nodeName.at(0) == '3' || nodeName.at(0) == '4'))
    {

      std::string fullName = g_Data.RowMap[nodeName];
      if (g_specialNodes && (std::regex_match(fullName.c_str(), cm, std::regex(specialNodes.c_str())) || g_companyKnownAddons[fullName].compare(specialNodes) == 0)
        && fullName.at(0) == 'a')
      {
        igraph_vector_push_back(&toDelete, i);
        std::cout << "Will delete " << nodeNamefromId(g, f, i) << " degree " << nodeDegree << " " << g_Data.RowMap[nodeNamefromId(g, f, i)] << std::endl;
        fprintf(f, "Will delete %s degree %d %s\n", nodeNamefromId(g, f, i).c_str(), nodeDegree, g_Data.RowMap[nodeNamefromId(g, f, i)].c_str());
        deletedNodes.push_back(nodeNamefromId(g, f, i));
        removed++;
        igraph_vector_t neighbor_edges;
        igraph_vector_init(&neighbor_edges, 0);
        igraph_incident(g, &neighbor_edges, i, IGRAPH_ALL);
        igraph_vector_append(&toDelete, &neighbor_edges);
      }
    }
  }

  fflush(ExcludeTargetNodesFile);
  fclose(ExcludeTargetNodesFile);
  std::cout << "Number of low frequency addon nodes: " << numlowfreqnodes << endl;
  std::cout << "Number of high frequency addon nodes: " << numhighfreqnodes << endl;

  if (g_bRemoveUsers || g_bRemoveTerms || g_removeSpecialNodes)
  {
    int delRes = igraph_delete_edges(g, igraph_ess_vector(&toDelete));
    std::cout << "Deleted edges from " << removed << " vertices" << std::endl;
  }
  else
  {
    if (igraph_delete_vertices(g, igraph_vss_vector(&toDelete)) != 0)
      std::cout << "Could not delete vertices" << std::endl;
    else
      std::cout << "Deleted " << removed << " vertices" << std::endl;
  }

  //Verify these nodes were deleted
  //igraph_vector_t typesAfterRemoval;
  //igraph_vector_init(&typesAfterRemoval, 0);
  //igraph_degree(g, &typesAfterRemoval, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS);
  //igraph_vector_print(&typesAfterRemoval);
  //igraph_vector_sort(&typesAfterRemoval);
  //igraph_vector_print(&typesAfterRemoval);
#ifdef _MY_DEBUG
  printAllNodesDegrees(f, g);
#endif // DEBUG

  //PrintMemoryInfo("End removeNodes");
  return deletedNodes;
}
void custom_warning_handler(const char *reason, const char *file,
  int line, int igraph_errno) {
  printf("My Warning: %s\n", reason);
}
void custom_igraph_error_handler_ignore(const char *reason, const char *file,
  int line, int igraph_errno) {
  //IGRAPH_UNUSED(reason);
  IGRAPH_UNUSED(file);
  IGRAPH_UNUSED(line);
  IGRAPH_UNUSED(igraph_errno);

  printf("My Error: %s\n", reason);

  IGRAPH_FINALLY_FREE();
}

struct CrtCheckMemory
{
  _CrtMemState state1;
  _CrtMemState state2;
  _CrtMemState state3;
  CrtCheckMemory()
  {
    _CrtMemCheckpoint(&state1);
  }
  ~CrtCheckMemory()
  {
    _CrtMemCheckpoint(&state2);
    // using google test you can just do this.
    //EXPECT_EQ(0, _CrtMemDifference(&state3, &state1, &state2));
    // else just do this to dump the leaked blocks to stdout.
    if (_CrtMemDifference(&state3, &state1, &state2))
      _CrtMemDumpStatistics(&state3);
  }
};

void Stl_To_Igraph_vector_t(std::vector<long int>& vectR, igraph_vector_t* v) {
  long n = (long)vectR.size();

  /* Make sure that there is enough space for the items in v */
  igraph_vector_resize(v, n);

  /* Copy all the items */
  for (size_t i = 0; i < n; i++) {
    VECTOR(*v)[i] = vectR[i];
  }
}

void Stl_To_Igraph_vector_ptr_t(std::vector< std::vector<long int> >& vectR, igraph_vector_ptr_t* vl) {
  long n = (long)vectR.size();

  /* Make sure that there is enough space for the items in vl */
  igraph_vector_ptr_resize(vl, n);

  /* Copy all the items */
  for (size_t i = 0; i < n; i++) {
    VECTOR(*vl)[i] = (igraph_vector_t*)malloc(sizeof(igraph_vector_t));
    igraph_vector_init((igraph_vector_t*)VECTOR(*vl)[i], 0);
    Stl_To_Igraph_vector_t(vectR[i], (igraph_vector_t*)VECTOR(*vl)[i]);
  }
}

void readKnownAddons()
{
  g_companyKnownAddons["a-prxtbhots.dll"] = ".*hotspot.*";
  
  g_companyKnownAddons["a-ie_virtual_keyboard_plugin.dll"] = ".*kaspersky.*";
  g_companyKnownAddons["a-ie_content_blocker_plugin.dll"] = ".*kaspersky.*";
  g_companyKnownAddons["a-online_banking_bho.dll"] = ".*kaspersky.*";
  g_companyKnownAddons["a-klwtbbho.dll_??????????????"] = ".*kaspersky.*";
  g_companyKnownAddons["a-ie_virtual_keyboard_plugin.dll_?????????????????????"] = ".*kaspersky.*";
  g_companyKnownAddons["a-ievkbd.dll_&virtualkeyboard"] = ".*kaspersky.*";
  g_companyKnownAddons["a-ievkbd.dll_clavier&virtuel"] = ".*kaspersky.*";
  
  g_companyKnownAddons["a-webassistant"] = ".*montiera.*";

  g_companyKnownAddons["a-navshext.dll"] = ".*norton.*";
  g_companyKnownAddons["a-nisshext.dll"] = ".*norton.*";

  g_companyKnownAddons["a-tbsoft.dll"] = ".*softonic.*";

  g_companyKnownAddons["a-easyvideodownloader.downloadvideofromanypage.downloadmp3orflashgames.downloadmusic.h._fvdvideodownloader"] = ".*snapdo.*";

  g_companyKnownAddons["a-tmbpie32.dll"] = ".*trendmicro.*";
  g_companyKnownAddons["a-toolbarie.dll"] = ".*trendmicro.*";
  g_companyKnownAddons["a-tmieplg32.dll"] = ".*trendmicro.*";
  g_companyKnownAddons["a-tmieplg.dll"] = ".*trendmicro.*";
  
  g_companyKnownAddons["a-trustcheckerieplugin.dll"] = ".*zonealarm.*";

}

bool comparePageRanks()
{
  std::cout << "\nRunning Compare\n" << comparePath << endl;
  if (!competitorsNodes.empty())
    std::cout << "Comparing with competitors nodes: " << competitorsNodes << endl;

  std::vector<pair<string, double>> vec;
  std::cout << "Reading PR ranks from " << globalePRpath << endl;
  vec = readNodesPR(globalePRpath);
  //if (!competitorsNodes.empty())
  //  vec = readNodesPR("C:\\addonsproject\\test\\GlobalPageRankNoPath.txt");
  //else
  //  vec = readNodesPR("C:\\addonsproject\\test\\GlobalPageRank4.txt");
  std::ifstream filehandle(comparePath);

  std::string line = "";
  int origPos = 1;
  FILE *comparisonFile;
  FILE *nodesOfInterestFile;
  FILE *comparisonFileRaw;
  FILE *matriceFile;

  std::string orig_recallpath = recallpath;
  string temp = orig_recallpath;
  string maricePath = opath;
  string nodesOfInterestFilePath = opath;
  replaceAll(maricePath, "PPR.txt", "Matrix.txt");
  replaceAll(nodesOfInterestFilePath, "PPR.txt", "NodesOfInterest.txt");
  //nodesOfInterestFile = fopen(temp.insert(temp.length() - 4, "NodesOfInterest").c_str(), "w");
  comparisonFileRaw = fopen(orig_recallpath.insert(orig_recallpath.length() - 4, "Comparison").c_str(), "w");
  comparisonFile = fopen(orig_recallpath.insert(orig_recallpath.length() - 4, "Competitors").c_str(), "w");
  
  matriceFile = fopen(maricePath.c_str(), "a");
  nodesOfInterestFile = fopen(nodesOfInterestFilePath.c_str(), "a");

  FILE *ExcludeLowFreqNodesFile;
  std::string tmp1path = temp;
  if (g_findExcludeNodes)
    ExcludeLowFreqNodesFile = fopen(tmp1path.insert(tmp1path.length() - 4, "ExcludeLowFreqNodes").c_str(), "w");
  else
    ExcludeLowFreqNodesFile = fopen(tmp1path.insert(tmp1path.length() - 4, "TempExclLowFreqNodes").c_str(), "w");

  if (orig_recallpath.find("NoPath") != std::string::npos)
  {
    //g_companyCompanyFreqSumMap["asktoolbar"] = 300911;
    //g_companyCompanyFreqSumMap["avg"] = 255680;
    //g_companyCompanyFreqSumMap["avira"] = 11737;
    //g_companyCompanyFreqSumMap["babylon"] = 287339;
    //g_companyCompanyFreqSumMap["blekko"] = 19213;
    //g_companyCompanyFreqSumMap["conduit"] = 22508;
    //g_companyCompanyFreqSumMap["googletoolbar"] = 213766;
    //g_companyCompanyFreqSumMap["hotspot"] = 4299;
    //g_companyCompanyFreqSumMap["imesh"] = 4364;
    //g_companyCompanyFreqSumMap["incredi"] = 92348;
    //g_companyCompanyFreqSumMap["montiera"] = 809;
    //g_companyCompanyFreqSumMap["norton"] = 27649;
    //g_companyCompanyFreqSumMap["searchtoolbar"] = 5337;
    //g_companyCompanyFreqSumMap["snapdo"] = 703;
    //g_companyCompanyFreqSumMap["softonic"] = 24068;
    //g_companyCompanyFreqSumMap["speedbit"] = 351533;
    //g_companyCompanyFreqSumMap["sweetim"] = 28135;
    //g_companyCompanyFreqSumMap["zonealarm"] = 4444;
    //g_companyCompanyFreqSumMap["trendmicro"] = 1060;

    g_companyCompanyFreqSumMap["asktoolbar"] = 350532;
    g_companyCompanyFreqSumMap["avg"] = 352051;
    g_companyCompanyFreqSumMap["avira"] = 17329;
    g_companyCompanyFreqSumMap["babylon"] = 921190;
    g_companyCompanyFreqSumMap["blekko"] = 19426;
    g_companyCompanyFreqSumMap["conduit"] = 22749;
    g_companyCompanyFreqSumMap["googletoolbar"] = 530996;
    g_companyCompanyFreqSumMap["hotspot"] = 75540;
    g_companyCompanyFreqSumMap["ilivid"] = 68181;
    g_companyCompanyFreqSumMap["imesh"] = 292034;
    g_companyCompanyFreqSumMap["incredi"] = 216371;
    g_companyCompanyFreqSumMap["kaspersky"] = 109001;
    g_companyCompanyFreqSumMap["montiera"] = 809;
    g_companyCompanyFreqSumMap["norton"] = 129758;
    g_companyCompanyFreqSumMap["searchtoolbar"] = 10004;
    g_companyCompanyFreqSumMap["snapdo"] = 703;
    g_companyCompanyFreqSumMap["softonic"] = 34724;
    g_companyCompanyFreqSumMap["speedbit"] = 2287264;
    g_companyCompanyFreqSumMap["sweetim"] = 106011;
    g_companyCompanyFreqSumMap["trendmicro"] = 22501;
    g_companyCompanyFreqSumMap["zonealarm"] = 4444;

  }
  else
  {
    g_companyCompanyFreqSumMap["asktoolbar"] = 300516;
    g_companyCompanyFreqSumMap["avg"] = 334715;
    g_companyCompanyFreqSumMap["avira"] = 11740;
    g_companyCompanyFreqSumMap["babylon"] = 284873;
    g_companyCompanyFreqSumMap["blekko"] = 18533;
    g_companyCompanyFreqSumMap["conduit"] = 22219;
    g_companyCompanyFreqSumMap["googletoolbar"] = 304611;
    g_companyCompanyFreqSumMap["hotspot"] = 74151;
    g_companyCompanyFreqSumMap["imesh"] = 38316;
    g_companyCompanyFreqSumMap["incredi"] = 93054;
    g_companyCompanyFreqSumMap["kaspersky"] = 189516;
    g_companyCompanyFreqSumMap["montiera"] = 810;
    g_companyCompanyFreqSumMap["norton"] = 117450;
    g_companyCompanyFreqSumMap["searchtoolbar"] = 5323;
    g_companyCompanyFreqSumMap["snapdo"] = 707;
    g_companyCompanyFreqSumMap["softonic"] = 29714;
    g_companyCompanyFreqSumMap["speedbit"] = 1157237;
    g_companyCompanyFreqSumMap["sweetim"] = 105433;
    g_companyCompanyFreqSumMap["trendmicro"] = 20895;
    g_companyCompanyFreqSumMap["zonealarm"] = 4091;
  }

  if (comparisonFileRaw == nullptr || comparisonFile == nullptr)
  {
    std::cout << "Couldn't open file" << endl;
    return 0;
  }
  if (!competitorsNodes.empty())
  {
    string tmp = specialNodes;
    replaceAll(tmp,string(".*"), "");
    //fprintf(comparisonFile, "\t\t\t\t\t\t\t\t\tTarget Company:\t%s\t%s\n", tmp, g_special_nodes_names.c_str());
    fprintf(comparisonFile, "%s\t%-8s\t  %-8s\t%-8s\t%-8s\t%s\t%s\t%s\t%s\n", "PPR Rank", "PR Rank", "Diff", "PPRScore", "PRScore", "Frequency", "Node Id", "Node name", "Company name");
    //fprintf(comparisonFileRaw, "\t\t\t\t\t\t\t\t\tTarget Company:\t%s\t%s\n", tmp, g_special_nodes_names.c_str());
    fprintf(comparisonFileRaw, "%s\t%-8s\t  %-8s\t%-8s %-8s\t%s\t%s\t%s\t%s\n", "PPR Rank", "PR Rank", "Diff", "PPRScore", "PRScore", "Frequency", "Node Id", "Node name", "Company name");
  }
  else
    fprintf(comparisonFile, "CurPos\t\tPRPos\t\tDIFF\t\tRank\t\tNode Id\t\tNode name\n");

  bool startSearching = false;
  while (std::getline(filehandle, line))
  {
    if (!startSearching)
    {
      if (line.find("TOP PAGE RANK") != string::npos)
      {
        startSearching = true;
      }
      continue;
    }
    std::stringstream line_ss(line);
    std::string column = "";
    unsigned int index = 0;
    std::string pr;
    std::string nodeId;
    std::string nodeName;
    int prPos = 0;
    while (std::getline(line_ss, column, '\t'))
    {
      prPos = 0;
      if (index == 0)
      {
        pr = column;
      }
      if (index == 1)
      {
        nodeId = column;
      }
      if (index == 2)
      {
        nodeName = column;
        //if not a user node
        if (!(nodeName.at(0) == 'u' && nodeName.length() > 1 && nodeName.at(1) == '-'))
        {
          auto cmp = [](std::pair<string, double> const & x, string d) -> bool
          { return x.first < d; };

          auto e = std::lower_bound(vec.begin(), vec.end(), nodeName, cmp);

          if (e == vec.end())
          {
            std::cout << "Couldn't find: " << nodeName << endl;
            prPos = origPos;
          }

          int pos = e->second;
          prPos = pos;
        }
        else //it's a user node
          prPos = origPos;
      }
      index++;
    }
    string competitorName = "UNKNOWN";
    //put only competitors addons
    if (!competitorsNodes.empty())
    {
      //addons only
      if (!nodeName.empty() && nodeName.at(0) == 'a')
      {

        std::cmatch cm1;
        std::string fullNameFullPath;
        bool bFoundInFullPath = false;
        bool bSpecialFoundInFullPath = false;
        //std::cout << "removing " << specialNodes << "from " << competitorsNodes << endl;
        //replaceAll(competitorsNodes, specialNodes, "");
        //std::cout << "after removal "  << competitorsNodes << endl;
        if (!std::regex_match(nodeName.c_str(), cm1, std::regex(competitorsNodes.c_str())))
        {
          //look for node in full path nodes
          string correctfullName = nodeName.substr(2);
          //check if exists in multimap of full paths
          auto fullnameRange = g_fullPathNodes.equal_range(correctfullName);
          for (auto itinner = fullnameRange.first; itinner != fullnameRange.second; ++itinner)
          {
            fullNameFullPath = itinner->second;
            std::cmatch cm;
            if (std::regex_match(fullNameFullPath.c_str(), cm, std::regex(competitorsNodes.c_str())))
            {
              //nodeName += "*";
              bFoundInFullPath = true;
              if (std::regex_match(fullNameFullPath.c_str(), cm, std::regex(specialNodes.c_str())))
              {
                std::cout << "Competitor Special node Extra: " << nodeName << " " << fullNameFullPath << endl;
                bSpecialFoundInFullPath = true;
              }
              else
                std::cout << "Competitor node Extra: " << nodeName << " " << fullNameFullPath << endl;

              break;
            }
          }
        }
        //end lookup

        std::cmatch cm;
        if (bFoundInFullPath || std::regex_match(nodeName.c_str(), cm, std::regex(competitorsNodes.c_str())))
        {
          for (auto it : competitorsNodesVec)
          {
            string companyName = it;
            replaceAll(companyName, string(".*"), "");
            if (nodeName.find(companyName) != string::npos || fullNameFullPath.find(companyName) != string::npos)
            {
              competitorName = companyName;
            }
          }
          //don't write the target node addon
          if (!std::regex_match(nodeName.c_str(), cm, std::regex(specialNodes.c_str())) && !bSpecialFoundInFullPath)
          {
            if (g_addonDataMap[nodeName].popularity > 100)
            {
              double origRank = -1;
              auto it = g_addonPageRankMap.find(nodeName);
              if (it != g_addonPageRankMap.end())
              {
                g_addonDataMap[nodeName].weighted_popularity = g_addonDataMap[nodeName].popularity / g_companyCompanyFreqSumMap[competitorName];
                origRank = it->second;
                g_companyTotalPageRankCounterMap[competitorName] += 1;

                g_companyPPRSumMap[competitorName] += origPos*g_addonDataMap[nodeName].weighted_popularity;
                g_companyPRSumMap[competitorName] += prPos*g_addonDataMap[nodeName].weighted_popularity;

                g_companyPPRFreqSumMap[competitorName] += g_addonDataMap[nodeName].popularity;
                g_companyPRFreqSumMap[competitorName] += g_addonDataMap[nodeName].popularity;

                g_companyTotalPageRankMap[competitorName] += it->second;
                g_companyCurPageRankMap[competitorName] += atof(pr.c_str());
                std::cout << "Weighted freq: " << g_addonDataMap[nodeName].weighted_popularity << ", " << nodeName << ", node freq: " << g_addonDataMap[nodeName].popularity << endl;
              }
              else
                std::cout << "Error: " << nodeName << endl;

              std::cout << "Frequency: " << nodeName << " : " << g_addonDataMap[nodeName].popularity << endl;
              fprintf(comparisonFile, "%-8d\t%-8d\t%-8d\t%s\t%-8f\t%-8d\t%s\t%s\t%s\n", origPos, prPos, origPos - prPos, pr.c_str(), origRank, g_addonDataMap[nodeName].popularity, nodeId.c_str(), nodeName.c_str(), competitorName.c_str());
            }
            else
            {
              fprintf(ExcludeLowFreqNodesFile, "%s\n", nodeName.c_str());
            }
          }
        }
        else if (g_addonDataMap[nodeName].popularity <= 100)
        {
          fprintf(ExcludeLowFreqNodesFile, "%s\n", nodeName.c_str());
        }
      }
    }
    //else
    {
      //fprintf(comparisonFile, "%-8d\t%-8d\t%-8d\t%s\t%s\t%s\n", origPos, prPos, origPos - prPos, pr.c_str(), nodeId.c_str(), nodeName.c_str());
      if (!nodeName.empty() && nodeName.at(0) == 'a')
      {
        double origRank = -1;
        auto it = g_addonPageRankMap.find(nodeName);
        if (it != g_addonPageRankMap.end())
        {
          origRank = it->second;
        }

        fprintf(comparisonFileRaw, "%-8d\t%-8d\t%-8d\t%s\t%-8f\t%-8d\t%s\t%s\t%s\n", origPos, prPos, origPos - prPos, pr.c_str(), origRank, g_addonDataMap[nodeName].popularity, nodeId.c_str(), nodeName.c_str(), competitorName.c_str());
        
        //Find dramatically changed nodes
        //very frequent (>100) 
        if (g_addonDataMap[nodeName].popularity > 100)
        {
          string firmname = specialNodes;
          replaceAll(firmname, ".*", "");
          int topN = 100;
          //PPR
          //Look at the top 100 addons in PPR
          if (origPos < topN + 1)
          {
            int diff = origPos - prPos;
            // in PPR have moved out of top 100
            if (diff < -topN || diff > topN)
            {
              std::cmatch cm;
              //Those that are not at known company list and
              if (!std::regex_match(nodeName.c_str(), cm, std::regex(competitorsNodes.c_str())))
              {
                std::cmatch cm;
                //Those that are not at the PPR company list
                if (!std::regex_match(nodeName.c_str(), cm, std::regex(specialNodes.c_str())))
                {
                  std::cout << "Interesting PPR: " << nodeName  << endl;
                  fprintf(nodesOfInterestFile, "%-8d\t%-8d\t%-8d\t%s\t%-8f\t%-8d\t%s\t%s\t%s\n", origPos, prPos, origPos - prPos, pr.c_str(), origRank, g_addonDataMap[nodeName].popularity, nodeId.c_str(), nodeName.c_str(), firmname.c_str());
                }
              }
            }
          }


          //PR
          int postionPR = 0;
          auto it = g_addonPRPositionMap.find(nodeName);
          if (it != g_addonPRPositionMap.end())
          {
            postionPR = it->second;
          }
          //Look at the top 100 addons in PR
          if (postionPR != 0 && postionPR < topN + 1)
          {
            int diff = postionPR - origPos;
            // in PR have moved out of top 100
            if (diff < -topN || diff > topN)
            {
              std::cmatch cm;
              //Those that are not at known company list and
              if (!std::regex_match(nodeName.c_str(), cm, std::regex(competitorsNodes.c_str())))
              {
                std::cmatch cm;
                //Those that are not at known company list and
                if (!std::regex_match(nodeName.c_str(), cm, std::regex(specialNodes.c_str())))
                {
                  std::cout << "Interesting PR: " << nodeName << endl;
                  fprintf(nodesOfInterestFile, "%-8d\t%-8d\t%-8d\t%s\t%-8f\t%-8d\t%s\t%s\t%s\n", origPos, prPos, origPos - prPos, pr.c_str(), origRank, g_addonDataMap[nodeName].popularity, nodeId.c_str(), nodeName.c_str(), firmname.c_str());
                }
              }
            }
          }
        }
      }
    }

    if (!nodeName.empty() && nodeName.at(0) == 'a')//advance rank only on addons
      origPos++;
  }

  for (auto it : g_companyTotalPageRankMap)
  {
    //std::cout << "Firm: " << it.first << "\tTotal entries: " << g_companyTotalPageRankCounterMap[it.first] << "\tPR score: " << it.second << "\tPPR score: " << g_companyCurPageRankMap[it.first] << "\tWeighted score: " << it.second / (g_companyTotalPageRankCounterMap[it.first]) << endl;
    
    //Sum of company addons aggregated ranks.
    //Each addon aggregated rank is addon_rank*addon_frequency divided by sum of all frequencies for this company

    //g_companyPPRSumMap[competitorName] += origPos;
    //g_companyPRSumMap[competitorName] += prPos;

    //g_companyPPRFreqSumMap[competitorName] += g_addonDataMap[nodeName].popularity;
    //g_companyPRFreqSumMap[competitorName] += g_addonDataMap[nodeName].popularity;

    //std::cout << "Firm: " << it.first << "\tTotal frequency: " << g_companyPPRFreqSumMap[it.first] << "\tPR weighted rank: " << g_companyPRSumMap[it.first] << "\tPPR weighted rank: " << g_companyPPRSumMap[it.first] << endl;
    string firmname = specialNodes;
    replaceAll(firmname, ".*", "");
    fprintf(matriceFile, "%s\t%s\t%-8f\t%-8f\n", firmname.c_str(), it.first.c_str(), g_companyPRSumMap[it.first], g_companyPPRSumMap[it.first]);
    
    std::cout << std::setprecision(9) << "g_companyCompanyFreqSumMap[\"" << it.first << "\"]=" << g_companyPPRFreqSumMap[it.first] << ";" << endl;
    //"\tFrequncy: " << g_addonDataMap[nodeName].popularity << "\tWeighted score: " << it.second / (g_companyTotalPageRankCounterMap[it.first]) << endl;
  }

  std::cout << "Company " << "CurPageRank " << "OriginalPageRank" << endl;
  for (auto it : g_companyTotalPageRankMap)
  {
    std::cout << it.first << " " << g_companyCurPageRankMap[it.first] / (g_companyTotalPageRankCounterMap[it.first]) << " " << it.second / (g_companyTotalPageRankCounterMap[it.first]) << endl;
  }

  fclose(comparisonFile);
  fclose(comparisonFileRaw);
  fclose(nodesOfInterestFile);
  fclose(matriceFile);
  fclose(ExcludeLowFreqNodesFile);

  return true;
}



int main(int argc, char* argv[]) {
  igraph_t g;
  igraph_vector_t y;
  igraph_strvector_t id;
  igraph_error_handler_t* oldhandler;
  igraph_warning_handler_t* oldwarnhandler;

 //_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
  //_CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDOUT);
  //_CrtMemState memstate1, memstate2, memstate3; // holds the memory states
  //_CrtMemCheckpoint(&memstate1); //take the memory snapshot
  //oldhandler = igraph_set_error_handler(igraph_error_handler_printignore);
  //oldwarnhandler = igraph_set_warning_handler(custom_warning_handler);
  //vector<pair<string, double>> vec;
  //vec.emplace_back("a", 100);
  //vec.emplace_back("b", 2);
  //vec.emplace_back("c", 3);
  //vec.emplace_back("c1", 4);
  //vec.emplace_back("ca", 5);
  //vec.emplace_back("d", 500);
  //vec.emplace_back("e", 34);
  //std::sort(vec.begin(), vec.end());
  //string str = "c";
  //auto cmp = [](std::pair<string, double> const & x, string d) -> bool
  //{ return x.first < d; };
  //auto elem = lower_bound(vec.begin(), vec.end(), str, cmp);
  //auto s = elem->first;
  //auto d = elem->second;
  //end test

  std::string userId;
  std::string addonId;
  std::string dirName;
  int maxDegree = -1;
  int topNodesNumToRemove = -1;
  bool bDebug = false;
  int directed = 0;

//  std::cmatch cm;
////  if (std::regex_match("test-123", cm, std::regex("subject.*|test.*")))
//  string name = "u-00004c1b-8ffc-4ca4-b406-a27e436fa34a";
//  string reg = ".*babylon.*";
//  if (std::regex_match(name.c_str(), cm, std::regex(reg)))
//    std::cout << "string literal matched " << cm.size() << " " << cm.str() << " " << cm.position() << endl;
//  std::cout << "the matches were: ";
//  for (unsigned i = 0; i < cm.size(); ++i) {
//    std::cout << "[" << cm[i] << "] ";
  //vector<string> v{ "test", "a-test2" };
  //int x = 0;
  ////auto f = std::find(vec.begin(), vec.end(), "test");
  //auto f = std::for_each(v.begin(), v.end(),
  //  [&](string& st) mutable
  //{
  //  if (st.find("test") != string::npos)
  //  {
  //    x++;
  //  }
  //});
//  }

  igraph_pagerank_power_options_t power_options;

  std::cout << "Start\n";
  /* turn on attribute handling */
  igraph_i_set_attribute_table(&igraph_cattribute_table);

  // Loop through each argument and print its number and value
  std::string cmd;
  for (int nArg = 0; nArg < argc; nArg++)
  {
    std::cout << argv[nArg] << " ";
    cmd.append(argv[nArg]);
    cmd.append(" ");
  }

  g_cmd = cmd;

  std::cout << "\n";

  if (argc < 13) {
    std::cout << "Usage is -dir <current directory> -i <input graph> -o1 <ppr output file> -o2 <ppr after addon removal output file>\n"
      << "-m <nodes map file> -u <user id node name(nodeMap ID) (it's will be the ppr vector)> -a (nodeMap ID)<addon node number to be removed from ppr vector>\n"
      << "-maxdegree <remove all nodes with degree higher than this> -topnodes <remove ~topnodes with highest degree>\n";
    std::cout << "The program will continue with default params\n";
  }
  else { // if we got enough parameters...
    for (int i = 1; i < argc; i++) {
      if (i + 1 != argc)
      {
        if (strcmp(argv[i], "-i") == 0) {
          ipath = argv[i + 1];
          ipath.insert(0, dirName);
        }
        else if (strcmp(argv[i], "-o1") == 0) {
          opath = argv[i + 1];
          opath.insert(0, dirName);
        }
        else if (strcmp(argv[i], "-o2") == 0) {
          opath2 = argv[i + 1];
          opath2.insert(0, dirName);
        }
        else if (strcmp(argv[i], "-rfilename") == 0) {
          recallpath = argv[i + 1];
          recallpath.insert(0, dirName);
        }
        else if (strcmp(argv[i], "-excelfilename") == 0) {
          excelpath = argv[i + 1];
          excelpath.insert(0, dirName);
        }
        else if (strcmp(argv[i], "-prfilename") == 0) {
          globalePRpath = argv[i + 1];
          globalePRpath.insert(0, dirName);
        }
        else if (strcmp(argv[i], "-m") == 0) {
          nodesMapPath = argv[i + 1];
          nodesMapPath.insert(0, dirName);
        }
        else if (strcmp(argv[i], "-u") == 0) {
          userId = argv[i + 1];
        }
        else if (strcmp(argv[i], "-a") == 0) {
          addonId = argv[i + 1];
        }
        else if (strcmp(argv[i], "-dir") == 0) {
          dirName = argv[i + 1];
          dirName += "\\";
        }
        else if (strcmp(argv[i], "-maxdegree") == 0) {
          std::string tmp = argv[i + 1];
          maxDegree = std::stoi(tmp);
        }
        else if (strcmp(argv[i], "-maxiter") == 0) {
          std::string tmp = argv[i + 1];
          g_maxiter = std::stoi(tmp);
        }
        else if (strcmp(argv[i], "-nruns") == 0) {
          std::string tmp = argv[i + 1];
          numberOfRuns = std::stoi(tmp);
          totalNRuns = numberOfRuns;
        }
        else if (strcmp(argv[i], "-topnodes") == 0) {
          std::string tmp = argv[i + 1];
          topNodesNumToRemove = std::stoi(tmp);
        }
        else if (strcmp(argv[i], "-minpprlength") == 0) {
          std::string tmp = argv[i + 1];
          g_minPPRVectorLenght = std::stoi(tmp);
        }
        else if (strcmp(argv[i], "-directed") == 0) {
          //std::string tmp = argv[i + 1];
          directed = 1;
        }
        else if (strcmp(argv[i], "-addonsonly") == 0) {
          //std::string tmp = argv[i + 1];
          g_addonsOnly = 1;
        }
        else if (strcmp(argv[i], "-removeterms") == 0) {
          //std::string tmp = argv[i + 1];
          g_bRemoveTerms = true;
        }
        else if (strcmp(argv[i], "-notrundom") == 0) {
          //std::string tmp = argv[i + 1];
          g_notRandom = true;
        }
        else if (strcmp(argv[i], "-prpack") == 0) {
          //std::string tmp = argv[i + 1];
          g_usePRPACK = true;
        }
        else if (strcmp(argv[i], "-globalpr") == 0) {
          //std::string tmp = argv[i + 1];
          g_useGlobalPR = true;
        }
        else if (strcmp(argv[i], "-compare") == 0) {
          comparePath = argv[i + 1];
          comparePath.insert(0, dirName);
          g_compare = true;
        }
        else if (strcmp(argv[i], "-specnodes") == 0) {
          //std::string tmp = argv[i + 1];
          g_specialNodes = true;
          specialNodes = argv[i + 1];
        }
        else if (strcmp(argv[i], "-removespecnodes") == 0) {
          //std::string tmp = argv[i + 1];
          g_removeSpecialNodes = true;
        }
        else if (strcmp(argv[i], "-findexcludenodes") == 0) {
          //std::string tmp = argv[i + 1];
          g_findExcludeNodes = true;
        }
        else if (strcmp(argv[i], "-competitornodes") == 0) {
          //std::string tmp = argv[i + 1];
          competitorsNodes = argv[i + 1];
          if (competitorsNodes.find("default") != std::string::npos)
            competitorsNodes = ".*asktoolbar.*|.*avg.*|.*avira.*|.*babylon.*|.*blekko.*|.*conduit.*|.*googletoolbar.*|.*imesh.*|.*incredi.*|.*ilivid.*|.*hotspot.*|.*kaspersky.*|.*montiera.*|.*norton.*|.*searchtoolbar.*|.*softonic.*|.*snapdo.*|.*speedbit.*|.*sweetim.*|.*trendmicro.*|.*zonealarm.*";
          competitorsNodesVec = split(competitorsNodes, '|');
          std::cout << "Competitors: " << competitorsNodes << endl;
        }
        else if (strcmp(argv[i], "-removeusers") == 0) {
          //std::string tmp = argv[i + 1];
          g_bRemoveUsers = true;
        }
        else if (strcmp(argv[i], "-finddup") == 0) {
          //std::string tmp = argv[i + 1];
          g_bFindDuplicates = true;
        }
        else if (strcmp(argv[i], "-popularityRank") == 0) {
          //std::string tmp = argv[i + 1];
          g_bPopularityRank = true;
        }
        else if (strcmp(argv[i], "-createVerbose") == 0) {
          //std::string tmp = argv[i + 1];
          g_bCreateVerboseGraph = true;
        }
        else if (strcmp(argv[i], "-debug") == 0) {
          bDebug = true;
          std::cout << "\nDebug mode!\n";
        }
        i++;
      }
    }
  }

  //string tmp = specialNodes;
  //replaceAll(tmp, string(".*"), "");

  std::cout << "Opening graph: " << ipath << std::endl;
  ifile = fopen(ipath.c_str(), "r");
  if (ifile == 0) {
    std::cout << "Failed opening file" << ipath;
    return 10;
  }

  //auto gpath = ipath.insert(ipath.length() - 4, "_altered");
  //std::cout << "Opening graph: " << gpath << std::endl;
  //gfile = fopen(gpath.c_str(), "w");
  //if (gfile == 0) {
  //  std::cout << "Failed opening file" << gpath;
  //  return 10;
  //}

  ofile = fopen(opath.c_str(), "w");
  if (ofile == 0) {
    std::cout << "Failed opening file" << opath;
    return 10;
  }

  ofileAfterRemoval = fopen(opath2.c_str(), "w");
  if (ofileAfterRemoval == 0) {
    std::cout << "Failed opening file" << opath2;
    return 10;
  }

  degreefile = fopen(degreePath.c_str(), "w");
  if (degreefile == 0) {
    std::cout << "Failed opening file" << degreePath;
    return 10;
  }

  //to delete on start
  recallfile = fopen(recallpath.c_str(), "w");
  fclose(recallfile);

  recallfile = fopen(recallpath.c_str(), "a");
  if (recallfile == 0) {
    std::cout << "Failed opening file" << recallpath;
    return 10;
  }

  //to delete on start
  if (excelpath.empty())
  {
    excelpath = recallpath;
    replaceAll(excelpath, ".txt", "Excel.xls");
  }
  excelfile = fopen(excelpath.c_str(), "w");
  fclose(excelfile);

  excelfile = fopen(excelpath.c_str(), "a");
  if (excelfile == 0) {
    std::cout << "Failed opening file" << excelpath;
    return 10;
  }

  string popularitypath = recallpath;
  replaceAll(popularitypath, ".txt", "Freq.txt");
  popularityfile = fopen(popularitypath.c_str(), "w");
  if (popularityfile == 0) {
    return 10;
  }

  string users_addons_map_file_path = recallpath;
  replaceAll(users_addons_map_file_path, ".txt", "UserAddonsUsed.txt");
  users_addons_map_file = fopen(users_addons_map_file_path.c_str(), "w");
  if (users_addons_map_file == 0) {
    return 10;
  }

  fprintf(recallfile, "##CMD: %s\n\n", cmd.c_str());

  auto startProgramRun = steady_clock::now();
  auto start = steady_clock::now();

  if (g_compare)
  {
    comparePageRanks();
    auto end = steady_clock::now();
    std::cout << "The whole run took "
      << duration_cast<std::chrono::seconds>(end - startProgramRun).count()
      << " secs.\n";
    return 0;
  }

  std::string orig_recallpath = recallpath;
  std::string orig_recallpath2 = recallpath;
  std::string orig_recallpath3 = recallpath;
  std::string orig_recallpath4 = recallpath;

  std::string orig_nodesMapPath = nodesMapPath;
  replaceAll(orig_nodesMapPath, "nodesMapNoPaths", "NodesMapNameToPath");
  readNodesMap(g_Data.RowMap, orig_nodesMapPath, true);

  //readExcludeFile(orig_recallpath2.insert(orig_recallpath2.length() - 4, "ExcludeTargetNodes"));
  //readExcludeFile(orig_recallpath3.insert(orig_recallpath3.length() - 4, "ExcludeLowFreqNodes"));
  //read_writeRawPRFile(globalePRpath);
  //read_writeRawComparisonFile(orig_recallpath.insert(orig_recallpath.length() - 4, "Comparison"));
  //read_writeRawComparisonCompetitorsFile(orig_recallpath4.insert(orig_recallpath4.length() - 4, "ComparisonCompetitors"));
  //return 0;
  
  
  readNodesMap(g_Data.RowMap, nodesMapPath);  

  readKnownAddons();

  //look for users that might be the same
  if (g_bFindDuplicates)
  {
    std::string dupsPath = degreePath;
    replaceAll(dupsPath, "RemovedAddonsDegree.txt", "DuplicateUsers.xls");
    dupUsersfile = fopen(dupsPath.c_str(), "w");
    if (dupUsersfile == 0) {
      std::cout << "Failed opening file" << dupsPath;
      return 10;
    }

    std::cout << "Looking for duplicate users\n";
    auto start = steady_clock::now();
    int dups = readGraphMap(ipath);
    auto end = steady_clock::now();
    std::cout << "Looking for duplicate users from " << ipath << " took "
      // duration_cast is required to avoid accidentally losing precision.
      << duration_cast<std::chrono::seconds>(end - start).count()
      << " sec.\n";
    std::cout << "Found " << dups << " users with same addons" << std::endl;
    fclose(dupUsersfile);
    //return 0;
  }

  if (g_notRandom)
  {
    std::string userAddonsMapPath = nodesMapPath;
    //replaceAll(userAddonsMapPath, "nodesMap", "userAddon");
    replaceAll(userAddonsMapPath, "nodesMap", "userAddonNoPath");
    readUserAddonsMap(g_Data.user_addons, userAddonsMapPath);
  }

  auto end = steady_clock::now();

  std::cout << "Loading nodes map from " << nodesMapPath << " took "
    << duration_cast<std::chrono::seconds>(end - start).count()
    << " sec.\n";

  start = steady_clock::now();
  std::cout << "Loading graph... (can take a few minutes) \n";

  igraph_read_graph_ncol(&g, ifile, NULL, 1, IGRAPH_ADD_WEIGHTS_IF_PRESENT, directed);
  //igraph_simplify(&g, 1, 1, 0);
  igraph_copy(&g_orig, &g);

  if (igraph_is_directed(&g))
    std::cout << "Graph IS directed\n";
  else
    std::cout << "Graph is NOT directed\n";

  end = steady_clock::now();
  std::cout << "Loading graph took "
    << duration_cast<std::chrono::seconds>(end - startProgramRun).count()
    << " sec.\n";

  igraph_vector_init(&gtypes, 0);
  igraph_vector_init(&vtypes, 0);
  igraph_vector_init(&etypes, 0);
  igraph_strvector_init(&gnames, 0);
  igraph_strvector_init(&vnames, 0);
  igraph_strvector_init(&enames, 0);

  igraph_cattribute_list(&g, &gnames, &gtypes, &vnames, &vtypes,
    &enames, &etypes);

#ifdef _MY_DEBUG
  print_attributes(&g, stdout);
  bDebug = true;
#endif

  if (g_useGlobalPR && !g_specialNodes)
  {
    igraph_arpack_options_t arpack_options;
    igraph_vector_t pprResult;
    igraph_vector_init(&pprResult, 0);
    igraph_arpack_options_init(&arpack_options);
    arpack_options.mxiter = g_maxiter;
    igraph_vs_t all;
    igraph_vs_all(&all);

    std::cout << "\nRegular PageRank Start\n";
    igraph_pagerank(&g_orig, IGRAPH_PAGERANK_ALGO_ARPACK, &pprResult, 0,
      all,
      directed, 0.85,
      0, &arpack_options);

    print_ppr_vector(&pprResult, ofileAfterRemoval, &vnames, &g, "pprAfterRemoval", NULL, true);

    return 0;
  }

  //remove high degree node
  g_deletedNodes = removeNodes(degreefile, &g, maxDegree, topNodesNumToRemove, &vnames);
  std::sort(g_popularity_rank_vector.begin(), g_popularity_rank_vector.end());
  auto it = std::unique(g_popularity_rank_vector.begin(), g_popularity_rank_vector.end());
  g_popularity_rank_vector.resize(std::distance(g_popularity_rank_vector.begin(), it));
  std::reverse(g_popularity_rank_vector.begin(), g_popularity_rank_vector.end());

  if (g_useGlobalPR && g_specialNodes && g_removeSpecialNodes)
  {
    igraph_arpack_options_t arpack_options;
    igraph_vector_t pprResult;
    igraph_vector_init(&pprResult, 0);
    igraph_arpack_options_init(&arpack_options);
    arpack_options.mxiter = g_maxiter;
    igraph_vs_t all;
    igraph_vs_all(&all);

    std::cout << "\nRegular PageRank Start\n";
    igraph_pagerank(&g, IGRAPH_PAGERANK_ALGO_ARPACK, &pprResult, 0,
      all,
      directed, 0.85,
      0, &arpack_options);

    print_ppr_vector(&pprResult, ofileAfterRemoval, &vnames, &g, "pprAfterRemoval", NULL, true);

    return 0;
  }

  //print sorted popularity
  std::vector<std::pair<std::string, int> > myVec(g_addonsPopularityMap.begin(), g_addonsPopularityMap.end());
  std::sort(myVec.begin(), myVec.end(),
    [](const std::pair<std::string, int>& lhs, const std::pair<std::string, int>& rhs) {
    return lhs.second > rhs.second; });
  for (auto it : myVec)
  {
    fprintf(popularityfile, "%s\t%d\n", it.first.c_str(), it.second);
  }

  //igraph_write_graph_ncol(&g, gfile, "name", 0);
  //fclose(gfile);

  //PrintMemoryInfo("Before loigraphNodeIDfromNodeMapIDop");
  igraph_vector_ptr_t vptrAddons;
  igraph_vector_ptr_init(&vptrAddons, 0);
  int addonIteratorPos = 0;
  do
  {
    int uid = 0;
    if (numberOfRuns > g_Data.user_addons.size() && g_Data.user_addons.size() > 0)
    {
      std::cout << "Need more users\n";
      return 44;
    }
    else if (g_Data.user_addons.size() > 0)
    {
      uid = std::stoi(g_Data.user_addons[addonIteratorPos].first);
      addonId = g_Data.user_addons[addonIteratorPos].second;
    }
    
    int vid = 837550;
    if (!g_specialNodes || g_special_nodes_vector.empty())
    {
      if (uid == 0)
      {
#ifdef _DEBUG
        uid = pickRandomNumberNotEqual(1000001, 1000008, g_deletedNodes, &g);
#else
        uid = pickRandomNumberNotEqual(1000001, 1907845, g_deletedNodes, &g_orig);
#endif
    }

      g_uid = uid;

      vid = igraphNodeIDfromNodeMapID(&g, stdout, uid);

      std::cout << "User ID node, igraph ID:" << vid << " ,nodeMap ID: " << uid << " degree " << igraph_degree_1(&g, vid) << " ,full name: " << g_Data.RowMap[std::to_string(uid)].c_str() << std::endl;

  }
    else
      std::cout << "!!!Using constant user id node, vid " << vid << endl;

    igraph_vs_t vsUid = igraph_vss_1(vid);

    ////test
    //igraph_vector_ptr_t vptrSubGraph;
    //igraph_vector_ptr_init(&vptrSubGraph, 0);
    //igraph_neighborhood_graphs(&g, &vptrSubGraph, vsUid, 1F, IGRAPH_ALL);
    //igraph_t *subGraph = (igraph_t *)VECTOR(vptrSubGraph)[0];
    //igraph_write_graph_edgelist(subGraph, stdout);
    ////print_attributes(subGraph, stdout);
    //igraph_write_graph_ncol(subGraph, stdout, "name", 0);
    ////end test

    igraph_vector_ptr_clear(&vptrAddons);
    igraph_vs_t vsAddonsReset;

    igraph_vector_t vAddons;
    igraph_vector_init(&vAddons, 0);
    if (g_special_nodes_vector.empty() && g_specialNodes)
    {
      std::cout << "No nodes found, exiting..." << endl;
      std::cout << "Verify that the string is a regex with .* notation: " << specialNodes << endl;
      auto end = steady_clock::now();
      std::cout << "The whole run took "
        << duration_cast<std::chrono::seconds>(end - startProgramRun).count()
        << " secs.\n";
      return 0;
    }
    if (g_specialNodes && !g_special_nodes_vector.empty())
    {
      igraph_real_t* a = (igraph_real_t*)&g_special_nodes_vector[0];
      std::cout << "Vector nodes\n";
      for (auto x : g_special_nodes_vector)
      {
        std::cout << x << ",";
      }
      std::cout << endl;
      //std::cout << "\nVector node done\n";

      Stl_To_Igraph_vector_t(g_special_nodes_vector, &vAddons);
      igraph_vs_vector_copy(&vsAddonsReset, &vAddons);
      //igraph_vs_vector_small(&vsAddonsReset, 1163868, 1163968, 1164110, 1164115, 1164566, 1164567, 1165257, 1165980, 1168152, 1170120, 1170488, 1170857, 1171020, 1174764, 1174781, 1174782, 1176398, 1178371, 1182359, 1182985, 1189725, 1192880, 1202476, 1202522, 1213516, 1215824, 1232872, 1248399, 1250204, 1264290, - 1);
      //500023,500024,500379,500386,500387,500389,501136,501137,501308,503370,504183,506624,509561,510075,510514,510768,510769,513064,516591,516626,516627,516628,518980,520809,521892,524671,528190,533710,539824,544831,551869,557405,560436,560438,568984,577008,578993,587275,594039,611931,619647,620133,628905,633456,638225,645218,653885,662463,664929,666476
      //igraph_vs_vector_small(&vsAddonsReset, 1163873, 1163874, 1164229, 1164236, 1164237, 1164239, 1164986, 1164987, 1165158, 1167220, 1168033, 1170474, 1173411, 1173925, 1174364, 1174618, 1174619, 1176914, 1180441, 1180476, 1180477, 1180478, 1182830, 1184659, 1185742, 1188521, 1192040, 1193035, 1197560, 1203674, 1208681, 1215719, 1221255, 1224286, 1224288, 1232834, 1240858, 1242843, 1246542, 1251125, 1257889, 1275781, 1283497, 1283983, 1292755, 1297306, 1302075, 1302867, 1305928, 1309068, 1317735, 1326313, 1328779, 1330326, -1);
      //1164412,1164413,1164790,1165759,1166825,1167523,1168395,1169813,1178395,1183067,1186256,1192546,1193090,1197008,1197186,1236806,1246462,1274865,1275051,1318027,1320839,1328816
      //igraph_vs_vector_small(&vsAddonsReset, 1164412, 1164413, 1164790, 1165759, 1166825, 1167523, 1168395, 1169813, 1178395, 1183067, 1186256, 1192546, 1193090, 1197008, 1197186, 1236806, 1246462, 1274865, 1275051, 1318027, 1320839, 1328816, -1);
      print_ppr_data(stdout, &g, &vsAddonsReset, "PPR vector");

      igraph_arpack_options_t arpack_options;
      igraph_vector_t pprResult;
      igraph_vector_init(&pprResult, 0);
      igraph_arpack_options_init(&arpack_options);
      arpack_options.mxiter = g_maxiter;
      igraph_vs_t all;
      igraph_vs_all(&all);

      igraph_personalized_pagerank_vs(&g, IGRAPH_PAGERANK_ALGO_ARPACK, &pprResult, 0,
        all,
        directed, 0.85,
        vsAddonsReset,
        0, &arpack_options);

      print_ppr_vector(&pprResult, ofileAfterRemoval, &vnames, &g, "pprAfterSpecial", &vsAddonsReset, true);

      comparePath = recallpath;
      comparePageRanks();
      std::string orig_recallpath = recallpath;
      std::string orig_recallpath4 = recallpath;

      readExcludeFile(orig_recallpath2.insert(orig_recallpath2.length() - 4, "ExcludeTargetNodes"));
      readExcludeFile(orig_recallpath3.insert(orig_recallpath3.length() - 4, "ExcludeLowFreqNodes"));
      read_writeRawComparisonFile(orig_recallpath.insert(orig_recallpath.length() - 4, "Comparison"));
      read_writeRawComparisonCompetitorsFile(orig_recallpath4.insert(orig_recallpath4.length() - 4, "ComparisonCompetitors"));

      auto end = steady_clock::now();
      std::cout << "The whole run took "
        << duration_cast<std::chrono::seconds>(end - startProgramRun).count()
        << " secs.\n";
      return 0;

    }
    else
    {
      if (g_bRemoveUsers || g_bRemoveTerms)
        igraph_neighbors(&g_orig, &vAddons, vid, IGRAPH_ALL);
      else
        igraph_neighbors(&g, &vAddons, vid, IGRAPH_ALL);
    }



    igraph_arpack_options_t arpack_options;
    igraph_vector_t pprResult;
    igraph_vector_init(&pprResult, 0);
    igraph_arpack_options_init(&arpack_options);
    arpack_options.mxiter = g_maxiter;

    if (g_useGlobalPR)
    {
      igraph_vs_t all;
      igraph_vs_all(&all);
      std::cout << "\nRegular PageRank\n";
      igraph_pagerank(&g_orig, IGRAPH_PAGERANK_ALGO_PRPACK, &pprResult, 0,
        all,
        directed, 0.85,
        0, &arpack_options);

      print_ppr_vector(&pprResult, ofileAfterRemoval, &vnames, &g, "pprAfterRemoval", NULL, true);

      return 0;
    }

    /* Personalized PageRank */

    //pick one of 1 to (size of PPR vector) cell
    int addonsNumber = 0;
    addonsNumber = igraph_vector_size(&vAddons);
    if (addonsNumber < g_minPPRVectorLenght - 1)
    {
      std::cout << "Only  " << addonsNumber << " addons, continue..." << '\n';
      addonIteratorPos++;
      numberOfRuns++;//do not count this run
      continue;
    }
    int elemPos = pickRandomNumber(0, addonsNumber);
    g_idVecToRemove = VECTOR(vAddons)[elemPos];
    //find the actual node id in graph
    if (!addonId.empty())
    {
      if (is_number(addonId))
      {
        long pos = 0;
        g_idVecToRemove = igraphNodeIDfromNodeMapID(&g, stdout, std::stoi(addonId));

        if (igraph_vector_search(&vAddons, 0, g_idVecToRemove, &pos))
          elemPos = pos;
        else
          std::cout << "Could not find " << g_idVecToRemove << " in the vector\n";
      }
    }


    igraph_vector_remove(&vAddons, elemPos);
    igraph_vs_t vsAddonsResetNew;
    igraph_vs_vector_copy(&vsAddonsResetNew, &vAddons);

    if (!g_useGlobalPR)
    {
      std::vector<std::string> pprAddons = print_ppr_data(stdout, &g, &vsAddonsReset, "Original PPR vector");

      /*Now, remove one of the PPR vector nodes*/
      //NEW PPR
      //(Step 1 - delete one addon from the personalization vector)


      std::cout << "Removed node igraph ID: " << g_idVecToRemove << " ,nodeMap ID:" << nodeNamefromId(&g, stdout, g_idVecToRemove) << " ,full name: " << g_Data.RowMap[nodeNamefromId(&g, stdout, g_idVecToRemove)] << std::endl;
      pprAddons.insert(pprAddons.begin(), g_Data.RowMap[nodeNamefromId(&g, stdout, g_idVecToRemove)]);
      fprintf(recallfile, "-----------REMOVED ADDON-------------\n%s %s\n", nodeNamefromId(&g, stdout, g_idVecToRemove).c_str(), g_Data.RowMap[nodeNamefromId(&g, stdout, g_idVecToRemove)].c_str());

      //Delete edge between the removed addon node and user node
      //(Step 2 - delete edge between user and addon)
      if (!g_bRemoveUsers)
      {
        print_nodes_connection_state(stdout, &g, vid, g_idVecToRemove);
        igraph_es_t es;
        igraph_es_path_small(&es, directed, vid, g_idVecToRemove, -1);
        int delRes = igraph_delete_edges(&g, es);
        igraph_es_destroy(&es);
        std::cout << "Delete edge " << vid << "-" << g_idVecToRemove << '\n';
        print_nodes_connection_state(stdout, &g, vid, g_idVecToRemove);
      }
    }


    start = steady_clock::now();
    //PrintMemoryInfo("Before PPR");
    igraph_vs_t all;
    igraph_vs_all(&all);
    if (g_usePRPACK)
    {
      std::cout << "\nPRPACK PageRank\n";
      igraph_personalized_pagerank_vs(&g, IGRAPH_PAGERANK_ALGO_PRPACK, &pprResult, 0,
        all,
        directed, 0.85,
        vsAddonsResetNew,
        0, 0);
    }
    else if (g_useGlobalPR)
    {
      std::cout << "\nRegular PageRank\n";
      igraph_pagerank(&g_orig, IGRAPH_PAGERANK_ALGO_ARPACK, &pprResult, 0,
        all,
        directed, 0.85,
        0, &arpack_options);
    }
    else
      std::cout << "\nARPACK PageRank\n";
    {
      igraph_personalized_pagerank_vs(&g, IGRAPH_PAGERANK_ALGO_ARPACK, &pprResult, 0,
        all,
        directed, 0.85,
        vsAddonsResetNew,
        0, &arpack_options);
    }

    igraph_vs_destroy(&all);

    //PrintMemoryInfo("After PPR");

    end = steady_clock::now();
    std::cout << "PPR after removal took "
      << duration_cast<std::chrono::seconds>(end - start).count()
      << " secs.\n";

    std::cout << "PPR after removal iterations " << arpack_options.noiter << '\n';
    if (!bDebug)
      print_ppr_vector(&pprResult, ofileAfterRemoval, &vnames, &g, "pprAfterRemoval", &vsAddonsResetNew, true);
    else
      print_ppr_vector(&pprResult, stdout, &vnames, &g, "pprAfterRemoval", &vsAddonsResetNew, true);

    igraph_vector_destroy(&pprResult);

    igraph_vs_destroy(&vsAddonsReset);
    igraph_vs_destroy(&vsAddonsResetNew);
    igraph_vs_destroy(&vsUid);
    igraph_vector_destroy(&vAddons);

    std::cout << "Left " << numberOfRuns << " runs\n";

    addonIteratorPos++;

  } while (--numberOfRuns > 0);

  igraph_vector_ptr_destroy_all(&vptrAddons);
  fclose(ifile);
  fclose(ofile);
  fclose(degreefile);
  fclose(recallfile);
  fclose(popularityfile);
  fclose(users_addons_map_file);

  igraph_vector_destroy(&gtypes);
  igraph_vector_destroy(&vtypes);
  igraph_vector_destroy(&etypes);
  igraph_strvector_destroy(&gnames);
  igraph_strvector_destroy(&vnames);
  igraph_strvector_destroy(&enames);

  end = steady_clock::now();
  std::cout << "The whole run took "
    << duration_cast<std::chrono::seconds>(end - startProgramRun).count()
    << " secs.\n";


  return 0;
#ifdef _MORE_
  ///* Copying a graph */
  //igraph_copy(&g2, &g);
  //print_attributes(&g2);
  //igraph_destroy(&g2);
  //
  ///* Adding vertices */
  //igraph_add_vertices(&g, 3, 0);
  //print_attributes(&g);
  //
  ///* Adding edges */
  //igraph_add_edge(&g, 1, 1);
  //igraph_add_edge(&g, 2, 5);
  //igraph_add_edge(&g, 3, 6);
  //print_attributes(&g);
  //
  ///* Deleting vertices */
  //igraph_delete_vertices(&g, igraph_vss_1(1));
  //igraph_delete_vertices(&g, igraph_vss_1(4));
  //print_attributes(&g);
  //
  ///* Deleting edges */
  //igraph_delete_edges(&g, igraph_ess_1(igraph_ecount(&g) - 1));
  //igraph_delete_edges(&g, igraph_ess_1(0));
  //print_attributes(&g);
  //
  ///* Set graph attributes */
  //SETGAN(&g, "id", 10);
  //if (GAN(&g, "id") != 10) {
  //  return 11;
  //}
  //SETGAS(&g, "name", "toy");
  //if (strcmp(GAS(&g, "name"), "toy")) {
  //  return 12;
  //}
  //
  ///* Delete graph attributes */
  //DELGA(&g, "id");
  //DELGA(&g, "name");
  //igraph_cattribute_list(&g, &gnames, 0, 0, 0, 0, 0);
  //if (igraph_strvector_size(&gnames) != 0) {
  //  return 14;
  //}
  //
  ///* Delete vertex attributes */
  //DELVA(&g, "x");
  //DELVA(&g, "shape");
  //DELVA(&g, "xfact");
  //DELVA(&g, "yfact");
  //igraph_cattribute_list(&g, 0, 0, &vnames, 0, 0, 0);
  //if (igraph_strvector_size(&vnames) != 2) {
  //  return 15;
  //}
  //
  ///* Delete edge attributes */
  //igraph_cattribute_list(&g, 0, 0, 0, 0, &enames, 0);
  //i = igraph_strvector_size(&enames);
  //DELEA(&g, "hook1");
  //DELEA(&g, "hook2");
  //DELEA(&g, "label");
  //igraph_cattribute_list(&g, 0, 0, 0, 0, &enames, 0);
  //if (igraph_strvector_size(&enames) != i - 3) {
  //  return 16;
  //}
  //
  ///* Set vertex attributes */
  //SETVAN(&g, "y", 0, -1);
  //SETVAN(&g, "y", 1, 2.1);
  //if (VAN(&g, "y", 0) != -1 ||
  //  VAN(&g, "y", 1) != 2.1) {
  //  return 17;
  //}
  //SETVAS(&g, "id", 0, "foo");
  //SETVAS(&g, "id", 1, "bar");
  //if (strcmp(VAS(&g, "id", 0), "foo") ||
  //  strcmp(VAS(&g, "id", 1), "bar")) {
  //  return 18;
  //}
  //
  ///* Set edge attributes */
  //SETEAN(&g, "weight", 2, 100.0);
  //SETEAN(&g, "weight", 0, -100.1);
  //if (EAN(&g, "weight", 2) != 100.0 ||
  //  EAN(&g, "weight", 0) != -100.1) {
  //  return 19;
  //}
  //SETEAS(&g, "color", 2, "RED");
  //SETEAS(&g, "color", 0, "Blue");
  //if (strcmp(EAS(&g, "color", 2), "RED") ||
  //  strcmp(EAS(&g, "color", 0), "Blue")) {
  //  return 20;
  //}
  //
  ///* Set vector attributes as vector */
  //igraph_vector_init(&y, igraph_vcount(&g));
  //igraph_vector_fill(&y, 1.23);
  //SETVANV(&g, "y", &y);
  //igraph_vector_destroy(&y);
  //for (i = 0; i<igraph_vcount(&g); i++) {
  //  if (VAN(&g, "y", i) != 1.23) {
  //    return 21;
  //  }
  //}
  //igraph_vector_init_seq(&y, 0, igraph_vcount(&g) - 1);
  //SETVANV(&g, "foobar", &y);
  //igraph_vector_destroy(&y);
  //for (i = 0; i<igraph_vcount(&g); i++) {
  //  if (VAN(&g, "foobar", i) != i) {
  //    return 22;
  //  }
  //}
  //
  //igraph_strvector_init(&id, igraph_vcount(&g));
  //for (i = 0; i<igraph_vcount(&g); i++) {
  //  sprintf_s(str, sizeof(str) - 1, "%li", i);
  //  igraph_strvector_set(&id, i, str);
  //}
  //SETVASV(&g, "foo", &id);
  //igraph_strvector_destroy(&id);
  //for (i = 0; i<igraph_vcount(&g); i++) {
  //  printf("%s ", VAS(&g, "foo", i));
  //}
  //printf("\n");
  //igraph_strvector_init(&id, igraph_vcount(&g));
  //for (i = 0; i<igraph_vcount(&g); i++) {
  //  sprintf_s(str, sizeof(str) - 1, "%li", i);
  //  igraph_strvector_set(&id, i, str);
  //}
  //SETVASV(&g, "id", &id);
  //igraph_strvector_destroy(&id);
  //for (i = 0; i<igraph_vcount(&g); i++) {
  //  printf("%s ", VAS(&g, "id", i));
  //}
  //printf("\n");
  //
  ///* Set edge attributes as vector */
  //igraph_vector_init(&y, igraph_ecount(&g));
  //igraph_vector_fill(&y, 12.3);
  //SETEANV(&g, "weight", &y);
  //igraph_vector_destroy(&y);
  //for (i = 0; i<igraph_ecount(&g); i++) {
  //  if (EAN(&g, "weight", i) != 12.3) {
  //    return 23;
  //  }
  //}
  //igraph_vector_init_seq(&y, 0, igraph_ecount(&g) - 1);
  //SETEANV(&g, "foobar", &y);
  //igraph_vector_destroy(&y);
  //for (i = 0; i<igraph_ecount(&g); i++) {
  //  if (VAN(&g, "foobar", i) != i) {
  //    return 24;
  //  }
  //}
  //
  //igraph_strvector_init(&id, igraph_ecount(&g));
  //for (i = 0; i<igraph_ecount(&g); i++) {
  //  sprintf_s(str, sizeof(str) - 1, "%li", i);
  //  igraph_strvector_set(&id, i, str);
  //}
  //SETEASV(&g, "foo", &id);
  //igraph_strvector_destroy(&id);
  //for (i = 0; i<igraph_ecount(&g); i++) {
  //  printf("%s ", EAS(&g, "foo", i));
  //}
  //printf("\n");
  //igraph_strvector_init(&id, igraph_ecount(&g));
  //for (i = 0; i<igraph_ecount(&g); i++) {
  //  sprintf_s(str, sizeof(str) - 1, "%li", i);
  //  igraph_strvector_set(&id, i, str);
  //}
  //SETEASV(&g, "color", &id);
  //igraph_strvector_destroy(&id);
  //for (i = 0; i<igraph_ecount(&g); i++) {
  //  printf("%s ", EAS(&g, "color", i));
  //}
  //printf("\n");
  //
  ///* Delete all remaining attributes */
  //DELALL(&g);
  //igraph_cattribute_list(&g, &gnames, &gtypes, &vnames, &vtypes, &enames, &etypes);
  //if (igraph_strvector_size(&gnames) != 0 ||
  //  igraph_strvector_size(&vnames) != 0 ||
  //  igraph_strvector_size(&enames) != 0) {
  //  return 25;
  //}
  //
  ///* Destroy */
  //igraph_vector_destroy(&gtypes);
  //igraph_vector_destroy(&vtypes);
  //igraph_vector_destroy(&etypes);
  //igraph_strvector_destroy(&gnames);
  //igraph_strvector_destroy(&vnames);
  //igraph_strvector_destroy(&enames);
  //
  //igraph_destroy(&g);
  //
  //return 0;
#endif
}
